[llvm-branch-commits] [llvm] [LLVM][docs] Finish MyST migration for remaining docs (batch 10) (PR #223099)

Reid Kleckner via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Sep 11 18:50:48 PDT 2026


https://github.com/rnk updated https://github.com/llvm/llvm-project/pull/223099

>From c2142e539d3ce69a14f4d57c8d1c37fc63aa54e0 Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Fri, 11 Sep 2026 16:06:44 +0000
Subject: [PATCH 1/3] [LLVM][docs] Convert remaining reST docs with rst2myst
 (batch 10)

---
 llvm/docs/AlignedBundling.md            |   79 +-
 llvm/docs/BlockFrequencyTerminology.md  |  102 +-
 llvm/docs/BranchWeightMetadata.md       |  254 ++--
 llvm/docs/BugLifeCycle.md               |  160 +--
 llvm/docs/CIBestPractices.md            |  153 +--
 llvm/docs/CodeReview.md                 |  122 +-
 llvm/docs/ConvergenceAndUniformity.md   |  703 +++++-----
 llvm/docs/ConvergentOperations.md       | 1683 +++++++++++------------
 llvm/docs/DependenceGraphs/index.md     |  104 +-
 llvm/docs/FaultMaps.md                  |  151 +-
 llvm/docs/FuzzingLLVM.md                |  289 ++--
 llvm/docs/GetElementPtr.md              |  380 +++--
 llvm/docs/GitBisecting.md               |  122 +-
 llvm/docs/GitHubActionsRunners.md       |   73 +-
 llvm/docs/GwpAsan.md                    |  268 ++--
 llvm/docs/HowToAddABuilder.md           |  498 +++----
 llvm/docs/HowToReleaseLLVM.md           |  417 +++---
 llvm/docs/HowToSetUpLLVMStyleRTTI.md    |  868 ++++++------
 llvm/docs/HowToSubmitABug.md            |  245 ++--
 llvm/docs/HowToUseInstrMappings.md      |  251 ++--
 llvm/docs/InAlloca.md                   |  197 ++-
 llvm/docs/InterfaceExportAnnotations.md |  487 ++++---
 llvm/docs/KernelInfo.md                 |   70 +-
 llvm/docs/LoopFusion.md                 |  128 +-
 llvm/docs/MLGO.md                       |  739 +++++-----
 25 files changed, 4081 insertions(+), 4462 deletions(-)

diff --git a/llvm/docs/AlignedBundling.md b/llvm/docs/AlignedBundling.md
index a7b47f936ef97..eec5c26b74226 100644
--- a/llvm/docs/AlignedBundling.md
+++ b/llvm/docs/AlignedBundling.md
@@ -1,9 +1,6 @@
-============================
-Aligned Instruction Bundling
-============================
+# Aligned Instruction Bundling
 
-Overview
-========
+## Overview
 
 *Aligned instruction bundling* partitions the instructions in a section into
 fixed-size, naturally aligned groups called bundles, and guarantees that no
@@ -18,26 +15,25 @@ control flow cannot jump into the middle of an instruction to manufacture a
 different, unchecked instruction sequence. When combined with masking of
 indirect branch targets to bundle-aligned addresses, this constrains all
 control flow to a statically verifiable set of locations and instructions.
-Bundling is used by the x86-64 implementation of :doc:`Lightweight Fault
+Bundling is used by the x86-64 implementation of {doc}`Lightweight Fault
 Isolation (LFI) <LFI>`.
 
-.. note::
+:::{note}
+The current LLVM implementation supports bundling only for x86 ELF targets.
+Other targets and object file formats reject the directives below rather
+than emit unbundled code silently.
+:::
 
-   The current LLVM implementation supports bundling only for x86 ELF targets.
-   Other targets and object file formats reject the directives below rather
-   than emit unbundled code silently.
+## `.bundle_align_mode`
 
-``.bundle_align_mode``
-======================
+```
+.bundle_align_mode abs-expr
+```
 
-::
-
-   .bundle_align_mode abs-expr
-
-Enables aligned bundle mode and sets the bundle size to ``2^abs-expr`` bytes,
-where ``abs-expr`` is a power-of-two exponent between 1 and 30 (as for the
-``.p2align`` directive). For example, ``.bundle_align_mode 5`` selects 32-byte
-bundles. Unlike GNU as, ``.bundle_align_mode 0`` is rejected: bundling cannot be
+Enables aligned bundle mode and sets the bundle size to `2^abs-expr` bytes,
+where `abs-expr` is a power-of-two exponent between 1 and 30 (as for the
+`.p2align` directive). For example, `.bundle_align_mode 5` selects 32-byte
+bundles. Unlike GNU as, `.bundle_align_mode 0` is rejected: bundling cannot be
 turned off once enabled.
 
 While bundling is enabled, the assembler ensures that no single instruction
@@ -51,16 +47,15 @@ receives instructions to at least the bundle size.
 Once enabled, bundle mode stays in effect for the rest of the file and its
 bundle size is fixed.
 
-``.bundle_lock`` and ``.bundle_unlock``
-=======================================
-
-::
+## `.bundle_lock` and `.bundle_unlock`
 
-   .bundle_lock [align_to_end]
-   ...instructions...
-   .bundle_unlock
+```
+.bundle_lock [align_to_end]
+...instructions...
+.bundle_unlock
+```
 
-A ``.bundle_lock`` / ``.bundle_unlock`` pair encloses a sequence of
+A `.bundle_lock` / `.bundle_unlock` pair encloses a sequence of
 instructions that must all be placed in a single bundle. The assembler inserts
 padding before the sequence, if necessary, so that the entire group lands
 within one bundle rather than straddling a boundary.
@@ -69,38 +64,36 @@ The enclosed sequence must fit within a single bundle -- it is an error if the
 total size of the locked instructions exceeds the bundle size.
 
 Both directives are only valid in an executable section after bundle mode has
-been enabled with ``.bundle_align_mode``. A ``.bundle_unlock`` must be matched
-by a preceding ``.bundle_lock``, and a section may not be switched while a
-``.bundle_lock`` is open. A group may not end in a bare instruction prefix,
+been enabled with `.bundle_align_mode`. A `.bundle_unlock` must be matched
+by a preceding `.bundle_lock`, and a section may not be switched while a
+`.bundle_lock` is open. A group may not end in a bare instruction prefix,
 which the assembler could not keep attached to the instruction it modifies.
 
-``align_to_end``
-----------------
+### `align_to_end`
 
 By default a locked group is padded at the front so that it starts far enough
-into the bundle to fit. With the ``align_to_end`` option the group is instead
+into the bundle to fit. With the `align_to_end` option the group is instead
 padded so that its last instruction ends exactly on a bundle boundary.
 
-Padding
-=======
+## Padding
 
 By default, bundle padding is emitted as no-op instructions, and neither an
 instruction nor a padding no-op is ever allowed to cross a bundle boundary.
 
-Prefix padding (x86)
---------------------
+### Prefix padding (x86)
 
 On x86 the assembler can instead absorb some of the required padding into
 neighboring instructions by prepending otherwise-ignored instruction prefixes
 to them, avoiding standalone no-ops. This is controlled by:
 
-.. option:: --x86-pad-max-prefix-size=<N>
-
-   Maximum number of prefixes the assembler may add to an instruction for
-   padding. ``0`` (the default) disables prefix padding, so only no-op
-   instructions are used.
+:::{option} --x86-pad-max-prefix-size=<N>
+Maximum number of prefixes the assembler may add to an instruction for
+padding. `0` (the default) disables prefix padding, so only no-op
+instructions are used.
+:::
 
 Padding is only ever traded for instruction bytes within the bundle that holds
 it, so this cannot move an instruction or a locked group across a boundary. It
 does change where labels land, so it may break assumptions about labels
 corresponding to particular instructions.
+
diff --git a/llvm/docs/BlockFrequencyTerminology.md b/llvm/docs/BlockFrequencyTerminology.md
index 878a10f35bd62..fb510ca03deae 100644
--- a/llvm/docs/BlockFrequencyTerminology.md
+++ b/llvm/docs/BlockFrequencyTerminology.md
@@ -1,128 +1,118 @@
-================================
-LLVM Block Frequency Terminology
-================================
+# LLVM Block Frequency Terminology
 
-
-Introduction
-============
+## Introduction
 
 Block Frequency is a metric for estimating the relative frequency of different
-basic blocks.  This document describes the terminology that the
-``BlockFrequencyInfo`` and ``MachineBlockFrequencyInfo`` analysis passes use.
+basic blocks. This document describes the terminology that the
+`BlockFrequencyInfo` and `MachineBlockFrequencyInfo` analysis passes use.
 
-Branch Probability
-==================
+## Branch Probability
 
 Blocks with multiple successors have probabilities associated with each
-outgoing edge.  These are called branch probabilities.  For a given block, the
+outgoing edge. These are called branch probabilities. For a given block, the
 sum of its outgoing branch probabilities should be 1.0.
 
-Branch Weight
-=============
+## Branch Weight
 
 Rather than storing fractions on each edge, we store an integer weight.
-Weights are relative to the other edges of a given predecessor block.  The
+Weights are relative to the other edges of a given predecessor block. The
 branch probability associated with a given edge is its own weight divided by
 the sum of the weights on the predecessor's outgoing edges.
 
 For example, consider this IR:
 
-.. code-block:: llvm
-
-   define void @foo() {
-       ; ...
-       A:
-           br i1 %cond, label %B, label %C, !prof !0
-       ; ...
-   }
-   !0 = !{!"branch_weights", i32 7, i32 8}
+```llvm
+define void @foo() {
+    ; ...
+    A:
+        br i1 %cond, label %B, label %C, !prof !0
+    ; ...
+}
+!0 = !{!"branch_weights", i32 7, i32 8}
+```
 
-and this simple graph representation::
+and this simple graph representation:
 
-   A -> B  (edge-weight: 7)
-   A -> C  (edge-weight: 8)
+```
+A -> B  (edge-weight: 7)
+A -> C  (edge-weight: 8)
+```
 
 The probability of branching from block A to block B is 7/15, and the
 probability of branching from block A to block C is 8/15.
 
-See :doc:`BranchWeightMetadata` for details about the branch weight IR
+See {doc}`BranchWeightMetadata` for details about the branch weight IR
 representation.
 
-Block Frequency
-===============
+## Block Frequency
 
 Block frequency is a relative metric that represents the number of times a
-block executes.  The ratio of a block frequency to the entry block frequency is
+block executes. The ratio of a block frequency to the entry block frequency is
 the expected number of times the block will execute per entry to the function.
 
-Block frequency is the main output of the ``BlockFrequencyInfo`` and
-``MachineBlockFrequencyInfo`` analysis passes.
+Block frequency is the main output of the `BlockFrequencyInfo` and
+`MachineBlockFrequencyInfo` analysis passes.
 
-Implementation: a series of DAGs
-================================
+## Implementation: a series of DAGs
 
 The implementation of the block frequency calculation analyses each loop,
-bottom-up, ignoring backedges; i.e., as a DAG.  After each loop is processed,
+bottom-up, ignoring backedges; i.e., as a DAG. After each loop is processed,
 it's packaged up to act as a pseudo-node in its parent loop's (or the
 function's) DAG analysis.
 
-Block Mass
-==========
+## Block Mass
 
-For each DAG, the entry node is assigned a mass of ``UINT64_MAX`` and mass is
-distributed to successors according to branch weights.  Block Mass uses a
-fixed-point representation where ``UINT64_MAX`` represents ``1.0`` and ``0``
-represents a number just above ``0.0``.
+For each DAG, the entry node is assigned a mass of `UINT64_MAX` and mass is
+distributed to successors according to branch weights. Block Mass uses a
+fixed-point representation where `UINT64_MAX` represents `1.0` and `0`
+represents a number just above `0.0`.
 
 After mass is fully distributed, in any cut of the DAG that separates the exit
 nodes from the entry node, the sum of the block masses of the nodes succeeded
-by a cut edge should equal ``UINT64_MAX``.  In other words, mass is conserved
+by a cut edge should equal `UINT64_MAX`. In other words, mass is conserved
 as it "falls" through the DAG.
 
 If a function's basic block graph is a DAG, then block masses are valid block
-frequencies.  This works poorly in practice though, since downstream users rely
+frequencies. This works poorly in practice though, since downstream users rely
 on adding block frequencies together without hitting the maximum.
 
-Loop Scale
-==========
+## Loop Scale
 
 Loop scale is a metric that indicates how many times a loop iterates per entry.
 As mass is distributed through the loop's DAG, the (otherwise ignored) backedge
-mass is collected.  This backedge mass is used to compute the exit frequency,
+mass is collected. This backedge mass is used to compute the exit frequency,
 and thus the loop scale.
 
-Implementation: Getting from mass and scale to frequency
-========================================================
+## Implementation: Getting from mass and scale to frequency
 
 After analysing the complete series of DAGs, each block has a mass (local to
 its containing loop, if any), and each loop pseudo-node has a loop scale and
 its own mass (from its parent's DAG).
 
 We can get an initial frequency assignment (with entry frequency of 1.0) by
-multiplying these masses and loop scales together.  A given block's frequency
+multiplying these masses and loop scales together. A given block's frequency
 is the product of its mass, the mass of containing loops' pseudo nodes, and the
 containing loops' loop scales.
 
 Since downstream users need integers (not floating point), this initial
-frequency assignment is shifted as necessary into the range of ``uint64_t``.
+frequency assignment is shifted as necessary into the range of `uint64_t`.
 
-Block Bias
-==========
+## Block Bias
 
 Block bias is a proposed *absolute* metric to indicate a bias toward or away
-from a given block during a function's execution.  The idea is that bias can be
+from a given block during a function's execution. The idea is that bias can be
 used in isolation to indicate whether a block is relatively hot or cold, or to
 compare two blocks to indicate whether one is hotter or colder than the other.
 
 The proposed calculation involves calculating a *reference* block frequency,
 where:
 
-* every branch weight is assumed to be 1 (i.e., every branch probability
+- every branch weight is assumed to be 1 (i.e., every branch probability
   distribution is even) and
-
-* loop scales are ignored.
+- loop scales are ignored.
 
 This reference frequency represents what the block frequency would be in an
 unbiased graph.
 
 The bias is the ratio of the block frequency to this reference block frequency.
+
diff --git a/llvm/docs/BranchWeightMetadata.md b/llvm/docs/BranchWeightMetadata.md
index 02742aa57266c..06d8412105c9b 100644
--- a/llvm/docs/BranchWeightMetadata.md
+++ b/llvm/docs/BranchWeightMetadata.md
@@ -1,88 +1,78 @@
-===========================
-LLVM Branch Weight Metadata
-===========================
+# LLVM Branch Weight Metadata
 
-
-Introduction
-============
+## Introduction
 
 Branch Weight Metadata represents branch weights as its likeliness to be taken
-(see :doc:`BlockFrequencyTerminology`). Metadata is assigned to a
-terminator ``Instruction`` as an ``MDNode`` of the ``MD_prof`` kind.
-The first operand is always an ``MDString`` node with the string
-"branch_weights".  The number of operands depends on the terminator type.
+(see {doc}`BlockFrequencyTerminology`). Metadata is assigned to a
+terminator `Instruction` as an `MDNode` of the `MD_prof` kind.
+The first operand is always an `MDString` node with the string
+"branch_weights". The number of operands depends on the terminator type.
 
 Branch weights might be fetched from the profiling file or generated based on
-`__builtin_expect`_ and `__builtin_expect_with_probability`_ instructions.
+[\_\_builtin_expect][__builtin_expect] and [\_\_builtin_expect_with_probability][__builtin_expect_with_probability] instructions.
 
 All weights are represented as unsigned 32-bit values, where a higher value
 indicates a greater chance of being taken.
 
-Supported Instructions
-======================
+## Supported Instructions
 
-``CondBrInst``
-^^^^^^^^^^^^^^
+### `CondBrInst`
 
 There are two extra operands for the true and the false branch.
-We optionally track if the metadata was added by ``__builtin_expect`` or
-``__builtin_expect_with_probability`` with an optional field ``!"expected"``.
-
-.. code-block:: none
+We optionally track if the metadata was added by `__builtin_expect` or
+`__builtin_expect_with_probability` with an optional field `!"expected"`.
 
-  !0 = !{
-    !"branch_weights",
-    [ !"expected", ]
-    i32 <TRUE_BRANCH_WEIGHT>,
-    i32 <FALSE_BRANCH_WEIGHT>
-  }
+```none
+!0 = !{
+  !"branch_weights",
+  [ !"expected", ]
+  i32 <TRUE_BRANCH_WEIGHT>,
+  i32 <FALSE_BRANCH_WEIGHT>
+}
+```
 
-``SwitchInst``
-^^^^^^^^^^^^^^
+### `SwitchInst`
 
-Branch weights are assigned to every case (including the ``default`` case, which
+Branch weights are assigned to every case (including the `default` case, which
 is always case #0).
 
-.. code-block:: none
-
-  !0 = !{
-    !"branch_weights",
-    [ !"expected", ]
-    i32 <DEFAULT_BRANCH_WEIGHT>
-    [ , i32 <CASE_BRANCH_WEIGHT> ... ]
-  }
+```none
+!0 = !{
+  !"branch_weights",
+  [ !"expected", ]
+  i32 <DEFAULT_BRANCH_WEIGHT>
+  [ , i32 <CASE_BRANCH_WEIGHT> ... ]
+}
+```
 
-``IndirectBrInst``
-^^^^^^^^^^^^^^^^^^
+### `IndirectBrInst`
 
 Branch weights are assigned to every destination.
 
-.. code-block:: none
+```none
+!0 = !{
+  !"branch_weights",
+  [ !"expected", ]
+  i32 <LABEL_BRANCH_WEIGHT>
+  [ , i32 <LABEL_BRANCH_WEIGHT> ... ]
+}
+```
 
-  !0 = !{
-    !"branch_weights",
-    [ !"expected", ]
-    i32 <LABEL_BRANCH_WEIGHT>
-    [ , i32 <LABEL_BRANCH_WEIGHT> ... ]
-  }
-
-``CallInst``
-^^^^^^^^^^^^^^^^^^
+### `CallInst`
 
 Calls may have branch weight metadata, containing the execution count of
 the call. It is currently used in SamplePGO mode only, to augment the
 block and entry counts, which may not be accurate with sampling.
 
-.. code-block:: none
-
-  !0 = !{
-    !"branch_weights",
-    [ !"expected", ]
-    i32 <CALL_BRANCH_WEIGHT>
-  }
+```none
+!0 = !{
+  !"branch_weights",
+  [ !"expected", ]
+  i32 <CALL_BRANCH_WEIGHT>
+}
+```
 
-``InvokeInst``
-^^^^^^^^^^^^^^^^^^
+### `InvokeInst`
 
 Invoke instruction may have branch weight metadata with one or two weights.
 The second weight is optional and corresponds to the unwind branch.
@@ -94,113 +84,104 @@ the count of unwind branch taken. Both weights specified are used to calculate
 BranchProbability as for CondBrInst and for SamplePGO the sum of both weights
 is used.
 
-.. code-block:: none
-
-  !0 = !{
-    !"branch_weights",
-    [ !"expected", ]
-    i32 <INVOKE_NORMAL_WEIGHT>
-    [ , i32 <INVOKE_UNWIND_WEIGHT> ]
-  }
+```none
+!0 = !{
+  !"branch_weights",
+  [ !"expected", ]
+  i32 <INVOKE_NORMAL_WEIGHT>
+  [ , i32 <INVOKE_UNWIND_WEIGHT> ]
+}
+```
 
-Other
-^^^^^
+### Other
 
 Other terminator instructions are not allowed to contain Branch Weight Metadata.
 
-.. _\__builtin_expect:
+(builtin-expect)=
 
-Built-in ``expect`` Instructions
-================================
+## Built-in `expect` Instructions
 
-``__builtin_expect(long exp, long c)`` instruction provides branch prediction
-information. The return value is the value of ``exp``.
+`__builtin_expect(long exp, long c)` instruction provides branch prediction
+information. The return value is the value of `exp`.
 
 It is especially useful in conditional statements. Currently Clang supports two
 conditional statements:
 
-``if`` statement
-^^^^^^^^^^^^^^^^
+### `if` statement
 
-The ``exp`` parameter is the condition. The ``c`` parameter is the expected
+The `exp` parameter is the condition. The `c` parameter is the expected
 comparison value. If it is equal to 1 (true), the condition is likely to be
 true, in other case condition is likely to be false. For example:
 
-.. code-block:: c++
+```c++
+if (__builtin_expect(x > 0, 1)) {
+  // This block is likely to be taken.
+}
+```
 
-  if (__builtin_expect(x > 0, 1)) {
-    // This block is likely to be taken.
-  }
+### `switch` statement
 
-``switch`` statement
-^^^^^^^^^^^^^^^^^^^^
-
-The ``exp`` parameter is the value. The ``c`` parameter is the expected
-value. If the expected value doesn't appear in the cases list, the ``default``
+The `exp` parameter is the value. The `c` parameter is the expected
+value. If the expected value doesn't appear in the cases list, the `default`
 case is assumed to be likely taken.
 
-.. code-block:: c++
-
-  switch (__builtin_expect(x, 5)) {
-  default: break;
-  case 0:  // ...
-  case 3:  // ...
-  case 5:  // This case is likely to be taken.
-  }
+```c++
+switch (__builtin_expect(x, 5)) {
+default: break;
+case 0:  // ...
+case 3:  // ...
+case 5:  // This case is likely to be taken.
+}
+```
 
-.. _\__builtin_expect_with_probability:
+(builtin-expect-with-probability)=
 
-Built-in ``expect.with.probability`` Instruction
-================================================
+## Built-in `expect.with.probability` Instruction
 
-``__builtin_expect_with_probability(long exp, long c, double probability)`` has
-the same semantics as ``__builtin_expect``, but the caller provides the
-probability that ``exp == c``. The last argument ``probability`` must be
+`__builtin_expect_with_probability(long exp, long c, double probability)` has
+the same semantics as `__builtin_expect`, but the caller provides the
+probability that `exp == c`. The last argument `probability` must be
 a constant floating-point expression and be in the range [0.0, 1.0] inclusive.
-The usage is also similar as ``__builtin_expect``, for example:
+The usage is also similar as `__builtin_expect`, for example:
 
-``if`` statement
-^^^^^^^^^^^^^^^^
+### `if` statement
 
-If the expected comparison value ``c`` is equal to 1(true), and probability
-value ``probability`` is set to 0.8, that means the probability of condition
+If the expected comparison value `c` is equal to 1(true), and probability
+value `probability` is set to 0.8, that means the probability of condition
 being true is 80% while that of false is 20%.
 
-.. code-block:: c++
-
-  if (__builtin_expect_with_probability(x > 0, 1, 0.8)) {
-    // This block is likely to be taken with probability 80%.
-  }
+```c++
+if (__builtin_expect_with_probability(x > 0, 1, 0.8)) {
+  // This block is likely to be taken with probability 80%.
+}
+```
 
-``switch`` statement
-^^^^^^^^^^^^^^^^^^^^
+### `switch` statement
 
-This is similar to the ``switch`` statement in ``__builtin_expect``.
-The probability that ``exp`` is equal to the expected value is given in
-the third argument ``probability``, while the probability of other value is
-the average of remaining probability(``1.0 - probability``). For example:
+This is similar to the `switch` statement in `__builtin_expect`.
+The probability that `exp` is equal to the expected value is given in
+the third argument `probability`, while the probability of other value is
+the average of remaining probability(`1.0 - probability`). For example:
 
-.. code-block:: c++
+```c++
+switch (__builtin_expect_with_probability(x, 5, 0.7)) {
+default: break;  // Take this case with probability 10%
+case 0:  break;  // Take this case with probability 10%
+case 3:  break;  // Take this case with probability 10%
+case 5:  break;  // This case is likely to be taken with probability 70%
+}
+```
 
-  switch (__builtin_expect_with_probability(x, 5, 0.7)) {
-  default: break;  // Take this case with probability 10%
-  case 0:  break;  // Take this case with probability 10%
-  case 3:  break;  // Take this case with probability 10%
-  case 5:  break;  // This case is likely to be taken with probability 70%
-  }
-
-CFG Modifications
-=================
+## CFG Modifications
 
 Branch Weight Metadata is not proof against CFG changes. If terminator operands'
 are changed, some action should be taken. Otherwise, misoptimizations may
 occur due to incorrect branch prediction information.
 
-Function Entry Counts
-=====================
+## Function Entry Counts
 
 To allow comparing different functions during inter-procedural analysis and
-optimization, ``MD_prof`` nodes can also be assigned to a function definition.
+optimization, `MD_prof` nodes can also be assigned to a function definition.
 The first operand is a string indicating the name of the associated counter.
 
 Currently, one counter is supported: "function_entry_count". The second operand
@@ -209,15 +190,15 @@ invoked (in the case of instrumentation-based profiles). In the case of
 sampling-based profiles, this operand is an approximation of how many times
 the function was invoked.
 
-For example, in the code below, the instrumentation for function ``foo()``
+For example, in the code below, the instrumentation for function `foo()`
 indicates that it was called 2,590 times at runtime.
 
-.. code-block:: llvm
-
-  define i32 @foo() !prof !1 {
-    ret i32 0
-  }
-  !1 = !{!"function_entry_count", i64 2590}
+```llvm
+define i32 @foo() !prof !1 {
+  ret i32 0
+}
+!1 = !{!"function_entry_count", i64 2590}
+```
 
 If "function_entry_count" has more than 2 operands, the subsequent operands are
 the GUID of the functions that need to be imported by ThinLTO. This is only
@@ -226,5 +207,6 @@ was collected on a binary that had already imported and inlined these functions,
 and we need to ensure the IR matches in the ThinLTO backends for profile
 annotation. The reason why we cannot annotate this on the callsite is that it
 can only go down 1 level in the call chain. For the cases where
-``foo_in_a_cc()->bar_in_b_cc()->baz_in_c_cc()``, we will need to go down 2 levels
-in the call chain to import both ``bar_in_b_cc`` and ``baz_in_c_cc``.
+`foo_in_a_cc()->bar_in_b_cc()->baz_in_c_cc()`, we will need to go down 2 levels
+in the call chain to import both `bar_in_b_cc` and `baz_in_c_cc`.
+
diff --git a/llvm/docs/BugLifeCycle.md b/llvm/docs/BugLifeCycle.md
index 5f02f7c550f49..e3cb8eea9cab3 100644
--- a/llvm/docs/BugLifeCycle.md
+++ b/llvm/docs/BugLifeCycle.md
@@ -1,12 +1,6 @@
-===================
-LLVM Bug Life Cycle
-===================
+# LLVM Bug Life Cycle
 
-
-
-
-Introduction - Achieving consistency in how we deal with bug reports
-====================================================================
+## Introduction - Achieving consistency in how we deal with bug reports
 
 We aim to achieve a basic level of consistency in how reported bugs evolve from
 being reported, to being worked on, and finally getting closed out. The
@@ -15,133 +9,123 @@ understanding of what a particular bug state actually means and what to expect
 might happen next.
 
 At the same time, we aim not to over-specify the life cycle of bugs in
-`the LLVM Bug Tracking System <https://github.com/llvm/llvm-project/issues>`_,
+[the LLVM Bug Tracking System](https://github.com/llvm/llvm-project/issues),
 as the overall goal is to make it easier to work with and understand the bug
 reports.
 
 The main parts of the life cycle documented here are:
 
-#. `Reporting`_
-#. `Triaging`_
-#. `Actively working on fixing`_
-#. `Closing`_
+1. [Reporting]
+2. [Triaging]
+3. [Actively working on fixing]
+4. [Closing]
 
 Furthermore, some of the metadata in the bug tracker, such as what labels we
 use, needs to be maintained. See the following for details:
 
-#. `Maintenance of metadata`_
+1. [Maintenance of metadata]
 
+(reporting)=
 
-.. _Reporting:
+## Reporting bugs
 
-Reporting bugs
-==============
+See {doc}`HowToSubmitABug` for further details on how to submit good bug reports.
 
-See :doc:`HowToSubmitABug` for further details on how to submit good bug reports.
-
-You can apply `labels <https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels>`_
+You can apply [labels](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels)
 to the bug to provide extra information to make the bug easier to discover, such
 as a label for the part of the project the bug pertains to.
 
-.. _Triaging:
+(triaging)=
 
-Triaging bugs
-=============
+## Triaging bugs
 
-Open bugs that have not been marked with the ``confirmed`` label
-still need to be triaged. When triage is complete, the ``confirmed`` label
+Open bugs that have not been marked with the `confirmed` label
+still need to be triaged. When triage is complete, the `confirmed` label
 should be added along with any other labels that help classify the report,
-unless the issue is being :ref:`closed<Closing>`.
+unless the issue is being {ref}`closed<Closing>`.
 
 The goal of triaging a bug is to make sure a newly reported bug ends up in a
 good, actionable state. Try to answer the following questions while triaging:
 
-* Is the reported behavior actually wrong?
+- Is the reported behavior actually wrong?
 
-  * E.g. does a miscompile example depend on undefined behavior?
+  - E.g. does a miscompile example depend on undefined behavior?
 
-* Can you reproduce the bug from the details in the report?
+- Can you reproduce the bug from the details in the report?
 
-  * If not, is there a reasonable explanation why it cannot be reproduced?
+  - If not, is there a reasonable explanation why it cannot be reproduced?
 
-* Is it related to an already reported bug?
+- Is it related to an already reported bug?
 
-* Are the following fields filled in correctly?
+- Are the following fields filled in correctly?
 
-  * Title
-  * Description
-  * Labels
+  - Title
+  - Description
+  - Labels
 
-* When able to do so, please add the appropriate labels to classify the bug,
-  such as the tool (``clang``, ``clang-format``, ``clang-tidy``, etc) or
-  component (``backend:<name>``, ``compiler-rt:<name>``, ``clang:<name>``, etc).
+- When able to do so, please add the appropriate labels to classify the bug,
+  such as the tool (`clang`, `clang-format`, `clang-tidy`, etc) or
+  component (`backend:<name>`, `compiler-rt:<name>`, `clang:<name>`, etc).
 
-* If the issue is with a particular revision of the C or C++ standard, please
-  add the appropriate language standard label (``c++20``, ``c99``, etc).
+- If the issue is with a particular revision of the C or C++ standard, please
+  add the appropriate language standard label (`c++20`, `c99`, etc).
 
-* Please don't use both a general and a specific label. For example, bugs
-  labeled ``c++17`` shouldn't also have ``c++``, and bugs labeled
-  ``clang:codegen`` shouldn't also have ``clang``.
+- Please don't use both a general and a specific label. For example, bugs
+  labeled `c++17` shouldn't also have `c++`, and bugs labeled
+  `clang:codegen` shouldn't also have `clang`.
 
-* Add the ``good first issue`` label if you think this would be a good bug to
-  be fixed by someone new to LLVM. This label feeds into `the landing page
-  for new contributors <https://github.com/llvm/llvm-project/contribute>`_.
+- Add the `good first issue` label if you think this would be a good bug to
+  be fixed by someone new to LLVM. This label feeds into [the landing page
+  for new contributors](https://github.com/llvm/llvm-project/contribute).
 
-* If you are unsure of what a label is intended to be used for, please see the
-  `documentation for our labels <https://github.com/llvm/llvm-project/labels>`_.
+- If you are unsure of what a label is intended to be used for, please see the
+  [documentation for our labels](https://github.com/llvm/llvm-project/labels).
 
-.. _Actively working on fixing:
+(actively-working-on-fixing)=
 
-Actively working on fixing bugs
-===============================
+## Actively working on fixing bugs
 
 Please remember to assign the bug to yourself if you're actively working on
-fixing it and to unassign it when you're no longer actively working on it.  You
-unassign a bug by removing the person from the ``Assignees`` field.
+fixing it and to unassign it when you're no longer actively working on it. You
+unassign a bug by removing the person from the `Assignees` field.
 
-.. _Closing:
+(closing)=
 
-Resolving/Closing bugs
-======================
+## Resolving/Closing bugs
 
 Resolving bugs is good! Make sure to properly record the reason for resolving.
 Examples of reasons for resolving are:
 
-  * If the issue has been resolved by a particular commit, close the issue with
-    a brief comment mentioning which commit(s) fixed it. If you are authoring
-    the fix yourself, your git commit message may include the phrase
-    ``Fixes #<issue number>`` on a line by itself. GitHub recognizes such commit
-    messages and will automatically close the specified issue with a reference
-    to your commit.
-
-  * If the reported behavior is not a bug, it is appropriate to close the issue
-    with a comment explaining why you believe it is not a bug, and adding the
-    ``invalid`` tag.
-
-  * If the bug duplicates another issue, close it as a duplicate by adding the
-    ``duplicate`` label with a comment pointing to the issue it duplicates.
-
-  * If there is a sound reason for not fixing the issue (difficulty, ABI, open
-    research questions, etc.), add the ``wontfix`` label and a comment explaining
-    why no changes are expected.
-
-  * If there is a specific and plausible reason to think that a given bug is
-    otherwise inapplicable or obsolete. One example is an open bug that doesn't
-    contain enough information to clearly understand the problem being reported
-    (e.g., not reproducible). It is fine to close such a bug, adding the
-    ``worksforme`` label and leaving a comment to encourage the reporter to
-    reopen the bug with more information if it's still reproducible for them.
-
-
-.. _Maintenance of metadata:
-
-Maintenance of metadata
-=======================
+> - If the issue has been resolved by a particular commit, close the issue with
+>   a brief comment mentioning which commit(s) fixed it. If you are authoring
+>   the fix yourself, your git commit message may include the phrase
+>   `Fixes #<issue number>` on a line by itself. GitHub recognizes such commit
+>   messages and will automatically close the specified issue with a reference
+>   to your commit.
+> - If the reported behavior is not a bug, it is appropriate to close the issue
+>   with a comment explaining why you believe it is not a bug, and adding the
+>   `invalid` tag.
+> - If the bug duplicates another issue, close it as a duplicate by adding the
+>   `duplicate` label with a comment pointing to the issue it duplicates.
+> - If there is a sound reason for not fixing the issue (difficulty, ABI, open
+>   research questions, etc.), add the `wontfix` label and a comment explaining
+>   why no changes are expected.
+> - If there is a specific and plausible reason to think that a given bug is
+>   otherwise inapplicable or obsolete. One example is an open bug that doesn't
+>   contain enough information to clearly understand the problem being reported
+>   (e.g., not reproducible). It is fine to close such a bug, adding the
+>   `worksforme` label and leaving a comment to encourage the reporter to
+>   reopen the bug with more information if it's still reproducible for them.
+
+(maintenance-of-metadata)=
+
+## Maintenance of metadata
 
 Project members with write access to the project can create new labels, but we
 discourage adding ad hoc labels because we want to control the proliferation of
 labels and avoid single-use labels. If you would like a new label added, please
-open an issue asking to create an issue label and add the ``infrastructure``
+open an issue asking to create an issue label and add the `infrastructure`
 label to the issue. The request should include a description of what the label
 is for. Alternatively, you can ask for the label to be created on the
-``#infrastructure`` channel on the LLVM Discord.
+`#infrastructure` channel on the LLVM Discord.
+
diff --git a/llvm/docs/CIBestPractices.md b/llvm/docs/CIBestPractices.md
index 2e5d378efc381..47b457186be22 100644
--- a/llvm/docs/CIBestPractices.md
+++ b/llvm/docs/CIBestPractices.md
@@ -1,25 +1,20 @@
-======================
-LLVM CI Best Practices
-======================
+# LLVM CI Best Practices
 
-Overview
-========
+## Overview
 
 This document contains a list of guidelines and best practices to use when
 working on LLVM's CI systems. These are intended to keep our actions reliable,
 consistent, and secure.
 
-GitHub Actions Best Practices
-=============================
+## GitHub Actions Best Practices
 
 This section contains information on best practices/guidelines when working on
 LLVM's GitHub actions workflows.
 
 For details on the runner pools these workflows execute on and their
-platform-specific constraints, see :doc:`GitHubActionsRunners`.
+platform-specific constraints, see {doc}`GitHubActionsRunners`.
 
-Disabling Jobs In Forks
------------------------
+### Disabling Jobs In Forks
 
 There are many LLVM forks that exist, and we currently default to preventing
 actions from running outside of the LLVM organization to prevent them from
@@ -31,23 +26,22 @@ otherwise.
 Adhering to this best practice looks like adding the following to each of the
 jobs specified within a workflow:
 
-.. code-block:: yaml
+```yaml
+jobs:
+  <job name>:
+    if: github.repository_owner == 'llvm'
+```
 
-  jobs:
-    <job name>:
-      if: github.repository_owner == 'llvm'
-
-We choose to use ``github.repository_owner`` rather than ``github.repository``
+We choose to use `github.repository_owner` rather than `github.repository`
 to enable these workflows to run in forks inside the LLVM organization, such as
 the ClangIR fork.
 
-There are some exceptions to this rule where ``github.repository`` might be
+There are some exceptions to this rule where `github.repository` might be
 used when it makes sense to limit a workflow to only running in the main
 monorepo repository. These include things like the issue subscriber and
 release tasks, which should not run anywhere else.
 
-Hash Pinning Dependencies
--------------------------
+### Hash Pinning Dependencies
 
 GitHub Actions allows the use of actions from other repositories as steps in
 jobs. We take advantage of various actions for a variety of different tasks,
@@ -55,11 +49,11 @@ but especially tasks like checking out the repository, and
 downloading/uploading build caches. These actions are typically versioned with
 just a release, which looks like the following:
 
-.. code-block:: yaml
-
-  steps:
-    - name: Checkout LLVM
-      uses: actions/checkout at v4
+```yaml
+steps:
+  - name: Checkout LLVM
+    uses: actions/checkout at v4
+```
 
 However, it is best practice to specify an exact commit SHA from which to pull
 the action, noting the version in a comment:
@@ -67,11 +61,11 @@ the action, noting the version in a comment:
 We plan on revisiting this recommendation once GitHub's immutable actions have
 been rolled out as GA.
 
-.. code-block:: yaml
-
-  steps:
-    - name: Checkout LLVM
-      uses: actions/checkout at 11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+```yaml
+steps:
+  - name: Checkout LLVM
+    uses: actions/checkout at 11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+```
 
 This is beneficial for two reasons: reliability and security. Specifying an
 exact SHA rather than just a major version ensures we end up running the same
@@ -84,124 +78,117 @@ within a specific version of an action after the fact, potentially stealing
 sensitive tokens and credentials. Hash pinning the dependencies prevents this
 as the hash would change with the code.
 
-Using Versioned Runner Images
------------------------------
+### Using Versioned Runner Images
 
 GitHub actions allows the use of either specifically versioned runner images
-(e.g., ``ubuntu-22.04``), or just the latest runner image
-(e.g., ``ubuntu-latest``). It is best practice to use explicitly versioned
+(e.g., `ubuntu-22.04`), or just the latest runner image
+(e.g., `ubuntu-latest`). It is best practice to use explicitly versioned
 runner images. This prevents breakages when GitHub rolls the latest runner
 image to a new version with potentially breaking changes, instead allowing us
 to explicitly opt-in to using the new image when we have done sufficient
 testing to ensure that our existing workflows work as expected in the new
 environment.
 
-Top Level Read Permissions
---------------------------
+### Top Level Read Permissions
 
 The top of every workflow should specify that the job only has read
 permissions:
 
-.. code-block:: yaml
-
-  permissions:
-    contents: read
+```yaml
+permissions:
+  contents: read
+```
 
 If specific jobs within the workflow need additional permissions, those
 permissions should be added within the specific job. This practice locks down
 all permissions by default and only enables them when needed, better enforcing
 the principle of least privilege.
 
-Ensuring Workflows Run on the Correct Events
---------------------------------------------
+### Ensuring Workflows Run on the Correct Events
 
 GitHub allows workflows to run on a multitude of events, and it is important to
 configure a workflow such that it triggers on the correct events. There are
 two main best practices around events that trigger workflows:
 
-1. Workflows that are designed to run on pull requests should not be
+1\. Workflows that are designed to run on pull requests should not be
 restricted by target branch. Restricting the target branch unnecessarily
-will prevent any stacked PRs from being tested. ``pull_request`` events should
+will prevent any stacked PRs from being tested. `pull_request` events should
 not contain a branches key.
 
-2. Workflows that are designed to also trigger on push events (e.g., for
-testing on ``main`` or one of the release branches) need to be restricted by
+2\. Workflows that are designed to also trigger on push events (e.g., for
+testing on `main` or one of the release branches) need to be restricted by
 branch. While pushes to a fork will not trigger a workflow run due to the
-``push`` event if the workflow already has its jobs disabled in forks
-(described above), stacked PRs will end up running jobs twice if the ``push``
-event does not have any branch restrictions. ``push`` events should have
-their branches restricted at the very least to ``main`` and the release
+`push` event if the workflow already has its jobs disabled in forks
+(described above), stacked PRs will end up running jobs twice if the `push`
+event does not have any branch restrictions. `push` events should have
+their branches restricted at the very least to `main` and the release
 branches as follows:
 
-.. code-block:: yaml
+```yaml
+push:
+  branches:
+    - main
+    - release/*
+```
 
-  push:
-    branches:
-      - main
-      - release/*
-
-Make Workflows Run on Updates to the Workflow Definition
---------------------------------------------------------
+### Make Workflows Run on Updates to the Workflow Definition
 
 Whenever possible, workflows should also run whenever the workflow definition
 is updated. This enables easily testing the workflow whenever modifying it. For
-example, if we have a workflow with a definition in ``.github/workflows/foo.yaml``,
+example, if we have a workflow with a definition in `.github/workflows/foo.yaml`,
 we should have at least the following event within the workflow:
 
-.. code-block:: yaml
-
-  pull_request:
-    paths:
-     - .github/workflows/foo.yaml
+```yaml
+pull_request:
+  paths:
+   - .github/workflows/foo.yaml
+```
 
 Note that it is not always possible to enable this (e.g., issues that use a
-``workflow_run`` trigger). But when possible, this makes testing the workflow
+`workflow_run` trigger). But when possible, this makes testing the workflow
 much simpler.
 
-Disable Credential Persistance
-------------------------------
+### Disable Credential Persistance
 
-Github's ``actions/checkout`` action will by default leave credentials from
+Github's `actions/checkout` action will by default leave credentials from
 the default Github token inside the git checkout it creates. This can present
 a security risk as someone might be able to exfiltrate the token if they are
 able to read any files within the git repository. This should be disabled by
 default as follows:
 
-.. code-block:: yaml
-
-  uses: actions/checkout@<commit SHA> # <version number>
-  with:
-    persist-credentials: false
+```yaml
+uses: actions/checkout@<commit SHA> # <version number>
+with:
+  persist-credentials: false
+```
 
 It is acceptable to leave credential persistence enabled if necessary, but one
 should be extra cautious when doing so.
 
-Container Best Practices
-========================
+## Container Best Practices
 
 This section contains best practices/guidelines when working with containers
 for LLVM infrastructure.
 
-Using Fully Qualified Container Names
--------------------------------------
+### Using Fully Qualified Container Names
 
 When referencing container images from a registry, such as in GitHub Actions
-workflows, or in ``Dockerfile`` files used for building images, prefer fully
+workflows, or in `Dockerfile` files used for building images, prefer fully
 qualified names (i.e., including the registry domain) over just the image.
-For example, prefer ``docker.io/ubuntu:24.04 at sha256:<sha>`` over
-``ubuntu:24.04 at sha256:<sha>``. This ensures portability across systems where a
+For example, prefer `docker.io/ubuntu:24.04 at sha256:<sha>` over
+`ubuntu:24.04 at sha256:<sha>`. This ensures portability across systems where a
 different default registry might be specified and also prevents attackers from
 changing the default registry to pull in a malicious image instead of the
 intended one.
 
-Hash-Pin Container Images
--------------------------
+### Hash-Pin Container Images
 
 Container images should be hash-pinned using the full SHA256 digest. For
-example, instead of writing ``docker.io/library/ubuntu:24.04``, one should
+example, instead of writing `docker.io/library/ubuntu:24.04`, one should
 write out
-``docker.io/library/ubuntu:24.04 at sha256:c4a8d5503dfb2a3eb8ab5f807da5bc69a85730fb49b5cfca2330194ebcc41c7b``.
+`docker.io/library/ubuntu:24.04 at sha256:c4a8d5503dfb2a3eb8ab5f807da5bc69a85730fb49b5cfca2330194ebcc41c7b`.
 This prevents images from getting updated when the tag changes, which can
 introduce security issues and unwanted behavior differences. This incurs
 some additional burden for updates, but most of these are automatically handled
 by Renovate.
+
diff --git a/llvm/docs/CodeReview.md b/llvm/docs/CodeReview.md
index f7672c225bff7..b18b4e29c87c9 100644
--- a/llvm/docs/CodeReview.md
+++ b/llvm/docs/CodeReview.md
@@ -1,31 +1,26 @@
-.. _code_review_policy:
+(code-review-policy)=
 
-=====================================
-LLVM Code-Review Policy and Practices
-=====================================
+# LLVM Code-Review Policy and Practices
 
 LLVM's code-review policy and practices help maintain high code quality across
 the project. Specifically, our code review process aims to:
 
- * Improve readability and maintainability.
- * Improve robustness and prevent the introduction of defects.
- * Best leverage the experience of other contributors for each proposed change.
- * Help grow and develop new contributors, through mentorship by community leaders.
+> - Improve readability and maintainability.
+> - Improve robustness and prevent the introduction of defects.
+> - Best leverage the experience of other contributors for each proposed change.
+> - Help grow and develop new contributors, through mentorship by community leaders.
 
 It is important for all contributors to understand our code-review
 practices and participate in the code-review process.
 
-General Policies
-================
+## General Policies
 
-What Code Should Be Reviewed?
------------------------------
+### What Code Should Be Reviewed?
 
 All developers are required to have significant changes reviewed before they
 are committed to the repository.
 
-Must Code Be Reviewed Prior to Being Committed?
------------------------------------------------
+### Must Code Be Reviewed Prior to Being Committed?
 
 Code can be reviewed either before it is committed or after. We expect
 significant patches to be reviewed before being committed. Smaller patches
@@ -38,15 +33,14 @@ Please note that the developer responsible for a patch is also
 responsible for making all necessary review-related changes, including
 those requested during any post-commit review.
 
-.. _post_commit_review:
+(post-commit-review)=
 
-Can Code Be Reviewed After It Is Committed?
--------------------------------------------
+### Can Code Be Reviewed After It Is Committed?
 
 Post-commit review is encouraged, and can be accomplished using any of the
 tools detailed below. There is a strong expectation that authors respond
 promptly to post-commit feedback and address it. Failure to do so is cause for
-the patch to be :ref:`reverted <revert_policy>`.
+the patch to be {ref}`reverted <revert_policy>`.
 
 If a community member expresses a concern about a recent commit, and this
 concern would have been significant enough to warrant a conversation during
@@ -77,41 +71,38 @@ original change was committed, it may be better to create a new patch to
 address the issues than comment on the original commit. The original patch
 author, for example, might no longer be an active contributor to the project.
 
-What Tools Are Used for Code Review?
-------------------------------------
+### What Tools Are Used for Code Review?
 
 Pre-commit code reviews are conducted on GitHub with Pull Requests. See
-:ref:`GitHub <github-reviews>` documentation.
+{ref}`GitHub <github-reviews>` documentation.
 
-When Is an RFC Required?
-------------------------
+### When Is an RFC Required?
 
 Some changes are too significant for just a code review. Changes that should
 change the LLVM Language Reference (e.g., adding new target-independent
 intrinsics), adding language extensions in Clang, and so on, require an RFC
-(Request for Comment) topic on the `LLVM Discussion Forums <https://discourse.llvm.org>`_
+(Request for Comment) topic on the [LLVM Discussion Forums](https://discourse.llvm.org)
 first. For changes that promise significant impact on users and/or downstream
 code bases, reviewers can request an RFC achieving consensus before proceeding
 with code review. That having been said, posting initial patches can help with
-discussions on an RFC. See the :doc:`RFC process <RFCProcess>` documentation
+discussions on an RFC. See the {doc}`RFC process <RFCProcess>` documentation
 for more details.
 
-Code-Review Workflow
-====================
+## Code-Review Workflow
 
 Code review can be an iterative process, which continues until the patch is
 ready to be committed. Specifically, once a patch is sent out for review, it
 needs an explicit approval before it is committed. Do not assume silent
 approval, or solicit objections to a patch with a deadline.
 
-.. note::
-   If you are using a Pull Request for purposes other than review
-   (eg: precommit CI results, convenient web-based reverts, etc)
-   `skip-precommit-approval <https://github.com/llvm/llvm-project/labels?q=skip-precommit-approval>`_
-   label to the PR.
+:::{note}
+If you are using a Pull Request for purposes other than review
+(eg: precommit CI results, convenient web-based reverts, etc)
+[skip-precommit-approval](https://github.com/llvm/llvm-project/labels?q=skip-precommit-approval)
+label to the PR.
+:::
 
-Acknowledge All Reviewer Feedback
----------------------------------
+### Acknowledge All Reviewer Feedback
 
 All comments by reviewers should be acknowledged by the patch author. It is
 generally expected that suggested changes will be incorporated into a future
@@ -126,13 +117,13 @@ commit message).
 If you suggest changes in a code review, but don't wish the suggestion to be
 interpreted this strongly, please state so explicitly.
 
-.. note::
-   After responding to reviewer comments,
-   press `Re-request review <https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review#:~:text=After%20your%20pull%20request%20is%20reviewed>`_
-   to bring the Pull Request to the reviewers' attention.
+:::{note}
+After responding to reviewer comments,
+press [Re-request review](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review#:~:text=After%20your%20pull%20request%20is%20reviewed)
+to bring the Pull Request to the reviewers' attention.
+:::
 
-Aim to Make Efficient Use of Everyone's Time
---------------------------------------------
+### Aim to Make Efficient Use of Everyone's Time
 
 Aim to limit the number of iterations in the review process. For example, when
 suggesting a change, if you want the author to make a similar set of changes at
@@ -143,10 +134,9 @@ from specific performance tests), please explain as many of these up front as
 possible. This allows the patch author and reviewers to make the most efficient
 use of their time.
 
-.. _lgtm_how_a_patch_is_accepted:
+(lgtm-how-a-patch-is-accepted)=
 
-LGTM - How a Patch Is Accepted
-------------------------------
+### LGTM - How a Patch Is Accepted
 
 A patch is approved to be committed when a reviewer accepts it, and this is
 almost always associated with a message containing the text "LGTM" (which
@@ -196,8 +186,7 @@ final approval should have commit access to the LLVM project.
 Every patch should be reviewed by at least one technical expert in the areas of
 the project affected by the change.
 
-Splitting Requests and Conditional Acceptance
----------------------------------------------
+### Splitting Requests and Conditional Acceptance
 
 Reviewers may request certain aspects of a patch to be broken out into separate
 patches for independent review. Reviewers may also accept a patch
@@ -207,8 +196,7 @@ project in a broken state). Moreover, reviewers can accept a patch conditioned o
 the author applying some set of minor updates prior to committing, and when
 applicable, it is polite for reviewers to do so.
 
-Don't Unintentionally Block a Review
-------------------------------------
+### Don't Unintentionally Block a Review
 
 If you review a patch, but don't intend for the review process to block on your
 approval, please state that explicitly. Out of courtesy, we generally wait on
@@ -216,11 +204,9 @@ committing a patch until all reviewers are satisfied, and if you don't intend
 to look at the patch again in a timely fashion, please communicate that fact in
 the review.
 
-Who Can/Should Review Code?
-===========================
+## Who Can/Should Review Code?
 
-Non-Experts Should Review Code
-------------------------------
+### Non-Experts Should Review Code
 
 You do not need to be an expert in some area of the code base to review patches;
 it's fine to ask questions about what some piece of code is doing. If it's not
@@ -235,38 +221,36 @@ the readability of the code in question. Structural changes, or further
 comments, may be appropriate.
 
 If you're new to the LLVM community, you might also find this presentation
-helpful: `How to Contribute to LLVM, A 2019 LLVM Developers' Meeting
-Presentation <https://youtu.be/C5Y977rLqpw>`_.
+helpful: [How to Contribute to LLVM, A 2019 LLVM Developers' Meeting
+Presentation](https://youtu.be/C5Y977rLqpw).
 
 A good way for new contributors to increase their knowledge of the code base is
 to review code. It is perfectly acceptable to review code and explicitly
 defer to others for approval decisions.
 
-Experts Should Review Code
---------------------------
+### Experts Should Review Code
 
 If you are an expert in an area of the compiler affected by a proposed patch,
 then you are highly encouraged to review the code. If you are a relevant
 maintainer, and no other experts are reviewing a patch, you must either help
 arrange for an expert to review the patch or review it yourself.
 
-Code Reviews, Speed, and Reciprocity
-------------------------------------
+### Code Reviews, Speed, and Reciprocity
 
 Sometimes code reviews will take longer than you might hope, especially for
 larger features. Common ways to speed up review times for your patches are:
 
-* Review other people's patches. If you help out, everybody will be more
+- Review other people's patches. If you help out, everybody will be more
   willing to do the same for you; goodwill is our currency.
-* Ping the patch. If it is urgent, provide reasons why it is important to you to
+- Ping the patch. If it is urgent, provide reasons why it is important to you to
   get this patch landed and ping it every couple of days. If it is
   not urgent, the common courtesy ping rate is one week. Remember that you're
   asking for valuable time from other professional developers.
-* Ask for help on Discord. Developers on Discord will be able to either help
+- Ask for help on Discord. Developers on Discord will be able to either help
   you directly, or tell you who might be a good reviewer.
-* Split your patch into multiple smaller patches that build on each other. The
+- Split your patch into multiple smaller patches that build on each other. The
   smaller your patch is, the higher the probability that somebody will take a quick
-  look at it. When doing this, it is helpful to add "[N/M]" (for 1 <= N <= M) to
+  look at it. When doing this, it is helpful to add "[N/M]" (for 1 \<= N \<= M) to
   the title of each patch in the series, so it is clear that there is an order
   and what that order is.
 
@@ -275,20 +259,20 @@ authors. If someone is kind enough to review your code, you should return the
 favor for someone else. Note that anyone is welcome to review and give feedback
 on a patch, but approval of patches should be consistent with the policy above.
 
-Upstreaming Changes to LLVM
-===========================
+## Upstreaming Changes to LLVM
 
 When upstreaming your own changes from a downstream project to LLVM, simply
 follow the process outlined above.
 
-When upstreaming changes originally written by someone else:  
+When upstreaming changes originally written by someone else:
 
-* Ensure that there are no obstacles to upstreaming the code. In some cases,
+- Ensure that there are no obstacles to upstreaming the code. In some cases,
   this simply means checking with the original author(s) to ensure they are
   aware of and approve the upstreaming. In other cases, licensing
   considerations may be more complex.
-* Properly attribute the original changes, e.g., by creating a commit with
-  multiple authors (`GitHub guide <https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors>`_).
-* Invite the original author(s) to review the changes, and also include
+- Properly attribute the original changes, e.g., by creating a commit with
+  multiple authors ([GitHub guide](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors)).
+- Invite the original author(s) to review the changes, and also include
   additional reviewers. Specifically, an LGTM from a (co-)author should not be
   taken as approval to land a change.
+
diff --git a/llvm/docs/ConvergenceAndUniformity.md b/llvm/docs/ConvergenceAndUniformity.md
index 3d333ba1128bd..91d2ab8ac3fe5 100644
--- a/llvm/docs/ConvergenceAndUniformity.md
+++ b/llvm/docs/ConvergenceAndUniformity.md
@@ -1,16 +1,12 @@
-.. _convergence-and-uniformity:
+(convergence-and-uniformity)=
 
-==========================
-Convergence And Uniformity
-==========================
+# Convergence And Uniformity
 
-
-Introduction
-============
+## Introduction
 
 In some environments, groups of threads execute the same program in parallel,
 where efficient communication within a group is established using special
-primitives called :ref:`convergent operations<convergent_operations>`. The
+primitives called {ref}`convergent operations<convergent_operations>`. The
 outcome of a convergent operation is sensitive to the set of threads that
 participate in it.
 
@@ -33,26 +29,25 @@ parallel environment. To eliminate this assumption:
 - We define convergence as a relation between the execution of each instruction
   by different threads and not as a relation between the threads themselves.
   This definition is reasonable for known targets and is compatible with the
-  semantics of :ref:`convergent operations<convergent_operations>` in LLVM IR.
+  semantics of {ref}`convergent operations<convergent_operations>` in LLVM IR.
 - We also define uniformity in terms of this convergence. The output of an
   instruction can be examined for uniformity across multiple threads only if the
   corresponding executions of that instruction are converged.
 
 This document describes a static analysis for determining convergence at each
 instruction in a function. The analysis extends previous work on divergence
-analysis [DivergenceSPMD]_ to cover irreducible control-flow. The described
+analysis [^cite_divergencespmd] to cover irreducible control-flow. The described
 analysis is used in LLVM to implement a UniformityAnalysis that determines the
 uniformity of value(s) computed at each instruction in an LLVM IR or MIR
 function.
 
-.. [DivergenceSPMD] Julian Rosemann, Simon Moll, and Sebastian
-   Hack. 2021. An Abstract Interpretation for SPMD Divergence on
-   Reducible Control Flow Graphs. Proc. ACM Program. Lang. 5, POPL,
-   Article 31 (January 2021), 35 pages.
-   https://doi.org/10.1145/3434312
+[^cite_divergencespmd]: Julian Rosemann, Simon Moll, and Sebastian
+    Hack. 2021. An Abstract Interpretation for SPMD Divergence on
+    Reducible Control Flow Graphs. Proc. ACM Program. Lang. 5, POPL,
+    Article 31 (January 2021), 35 pages.
+    <https://doi.org/10.1145/3434312>
 
-Motivation
-==========
+## Motivation
 
 Divergent branches constrain
 program transforms such as changing the CFG or moving a convergent
@@ -75,32 +70,35 @@ subgroups):
   branches, since the whole group of threads follows either one side
   of the branch or the other.
 
-Terminology
-===========
+## Terminology
 
 Cycles
-   Described in :ref:`cycle-terminology`.
+
+: Described in {ref}`cycle-terminology`.
 
 Closed path
-   Described in :ref:`cycle-closed-path`.
+
+: Described in {ref}`cycle-closed-path`.
 
 Disjoint paths
-   Two paths in a CFG are said to be disjoint if the only nodes common
-   to both are the start node or the end node, or both.
+
+: Two paths in a CFG are said to be disjoint if the only nodes common
+  to both are the start node or the end node, or both.
 
 Join node
-   A join node of a branch is a node reachable along disjoint paths
-   starting from that branch.
+
+: A join node of a branch is a node reachable along disjoint paths
+  starting from that branch.
 
 Diverged path
-   A diverged path is a path that starts from a divergent branch and
-   either reaches a join node of the branch or reaches the end of the
-   function without passing through any join node of the branch.
 
-.. _convergence-dynamic-instances:
+: A diverged path is a path that starts from a divergent branch and
+  either reaches a join node of the branch or reaches the end of the
+  function without passing through any join node of the branch.
+
+(convergence-dynamic-instances)=
 
-Threads and Dynamic Instances
-=============================
+## Threads and Dynamic Instances
 
 Each occurrence of an instruction in the program source is called a
 *static instance*. When a thread executes a program, each execution of
@@ -118,9 +116,11 @@ Each thread produces a unique sequence of dynamic instances:
 Threads are independent; some targets may choose to execute them in
 groups in order to share resources when possible.
 
-.. figure:: convergence-natural-loop.png
-   :name: convergence-natural-loop
+:::{figure} convergence-natural-loop.png
+:name: convergence-natural-loop
+:::
 
+```{eval-rst}
 .. table::
    :name: convergence-thread-example
    :align: left
@@ -132,32 +132,33 @@ groups in order to share resources when possible.
    +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
    | Thread 2 | Entry1 | H2  |     | L2  | H4  | B2  | L4  | H5  | B3  | L5  | Exit |
    +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+```
 
 In the above table, each row is a different thread, listing the
 dynamic instances produced by that thread from left to right. Each
-thread executes the same program that starts with an ``Entry`` node
-and ends with an ``Exit`` node, but different threads may take
+thread executes the same program that starts with an `Entry` node
+and ends with an `Exit` node, but different threads may take
 different paths through the control flow of the program. The columns
 are numbered merely for convenience, and empty cells have no special
 meaning. Dynamic instances listed in the same column are converged.
 
-.. _convergence-definition:
+(convergence-definition)=
 
-Convergence
-===========
+## Convergence
 
 *Convergence-before* is a strict partial order over dynamic instances
 that is defined as the transitive closure of:
 
-1. If dynamic instance ``P`` is executed strictly before ``Q`` in the
-   same thread, then ``P`` is *convergence-before* ``Q``.
-2. If dynamic instance ``P`` is executed strictly before ``Q1`` in the
-   same thread, and ``Q1`` is *converged-with* ``Q2``, then ``P`` is
-   *convergence-before* ``Q2``.
-3. If dynamic instance ``P1`` is *converged-with* ``P2``, and ``P2``
-   is executed strictly before ``Q`` in the same thread, then ``P1``
-   is *convergence-before* ``Q``.
+1. If dynamic instance `P` is executed strictly before `Q` in the
+   same thread, then `P` is *convergence-before* `Q`.
+2. If dynamic instance `P` is executed strictly before `Q1` in the
+   same thread, and `Q1` is *converged-with* `Q2`, then `P` is
+   *convergence-before* `Q2`.
+3. If dynamic instance `P1` is *converged-with* `P2`, and `P2`
+   is executed strictly before `Q` in the same thread, then `P1`
+   is *convergence-before* `Q`.
 
+```{eval-rst}
 .. table::
    :name: convergence-order-example
    :align: left
@@ -171,12 +172,13 @@ that is defined as the transitive closure of:
    +----------+-------+-----+-----+-----+-----+-----+-----+-----+------+
    | Thread 3 | Entry | ... | P   | Q1  |     |     |     | ... |      |
    +----------+-------+-----+-----+-----+-----+-----+-----+-----+------+
+```
 
 The above table shows partial sequences of dynamic instances from
 different threads. Dynamic instances in the same column are assumed
 to be converged (i.e., related to each other in the converged-with
-relation). The resulting convergence order includes the edges ``P ->
-Q2``, ``Q1 -> R``, ``P -> R``, ``P -> T``, etc.
+relation). The resulting convergence order includes the edges `P ->
+Q2`, `Q1 -> R`, `P -> R`, `P -> T`, etc.
 
 *Converged-with* is a transitive symmetric relation over dynamic instances
 produced by *different threads* for the *same static instance*.
@@ -186,31 +188,28 @@ relation, since different environments may wish to relate dynamic instances in
 different ways. The fact that *convergence-before* is a strict partial order is
 a constraint on the *converged-with* relation. It is trivially satisfied if
 different dynamic instances are never converged. Below, we provide a relation
-called :ref:`maximal converged-with<convergence-maximal>`, which satisifies
+called {ref}`maximal converged-with<convergence-maximal>`, which satisifies
 *convergence-before* and is suitable for known targets.
 
-.. _convergence-note-convergence:
+(convergence-note-convergence)=
 
-.. note::
+:::{note}
+1. The convergence-before relation is not
+   directly observable. Program transforms are in general free to
+   change the order of instructions, even though that obviously
+   changes the convergence-before relation.
+2. Converged dynamic instances need not be executed at the same
+   time or even on the same resource. Converged dynamic instances
+   of a convergent operation may appear to do so but that is an
+   implementation detail.
+3. The fact that `P` is convergence-before
+   `Q` does not automatically imply that `P` happens-before
+   `Q` in a memory model sense.
+:::
 
-   1. The convergence-before relation is not
-      directly observable. Program transforms are in general free to
-      change the order of instructions, even though that obviously
-      changes the convergence-before relation.
+(convergence-maximal)=
 
-   2. Converged dynamic instances need not be executed at the same
-      time or even on the same resource. Converged dynamic instances
-      of a convergent operation may appear to do so but that is an
-      implementation detail.
-
-   3. The fact that ``P`` is convergence-before
-      ``Q`` does not automatically imply that ``P`` happens-before
-      ``Q`` in a memory model sense.
-
-.. _convergence-maximal:
-
-Maximal Convergence
--------------------
+### Maximal Convergence
 
 This section defines a constraint that may be used to
 produce a *maximal converged-with* relation without violating the
@@ -225,33 +224,35 @@ a cycle if they both previously executed the cycle header the same number of
 times after they entered that cycle. In general, this needs to account for the
 iterations of parent cycles as well.
 
-   **Maximal converged-with:**
-
-   Dynamic instances ``X1`` and ``X2`` produced by different threads
-   for the same static instance ``X`` are converged in the maximal
-   converged-with relation if and only if:
-
-   - ``X`` is not contained in any cycle, or,
-   - For every cycle ``C`` with header ``H`` that contains ``X``:
-
-     - every dynamic instance ``H1`` of ``H`` that precedes ``X1`` in
-       the respective thread is convergence-before ``X2``, and,
-     - every dynamic instance ``H2`` of ``H`` that precedes ``X2`` in
-       the respective thread is convergence-before ``X1``,
-     - without assuming that ``X1`` is converged with ``X2``.
-
-.. note::
-
-   Cycle headers may not be unique to a given CFG if it is irreducible. Each
-   cycle hierarchy for the same CFG results in a different maximal
-   converged-with relation.
-
-   For brevity, the rest of the document restricts the term
-   *converged* to mean "related under the maximal converged-with
-   relation for the given cycle hierarchy".
+> **Maximal converged-with:**
+>
+> Dynamic instances `X1` and `X2` produced by different threads
+> for the same static instance `X` are converged in the maximal
+> converged-with relation if and only if:
+>
+> - `X` is not contained in any cycle, or,
+>
+> - For every cycle `C` with header `H` that contains `X`:
+>
+>   - every dynamic instance `H1` of `H` that precedes `X1` in
+>     the respective thread is convergence-before `X2`, and,
+>   - every dynamic instance `H2` of `H` that precedes `X2` in
+>     the respective thread is convergence-before `X1`,
+>   - without assuming that `X1` is converged with `X2`.
+
+:::{note}
+Cycle headers may not be unique to a given CFG if it is irreducible. Each
+cycle hierarchy for the same CFG results in a different maximal
+converged-with relation.
+
+For brevity, the rest of the document restricts the term
+*converged* to mean "related under the maximal converged-with
+relation for the given cycle hierarchy".
+:::
 
 Maximal convergence can now be demonstrated in the earlier example as follows:
 
+```{eval-rst}
 .. table::
    :align: left
 
@@ -262,178 +263,178 @@ Maximal convergence can now be demonstrated in the earlier example as follows:
    +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
    | Thread 2 | Entry2 | H2  |     | L2  | H4  | B2  | L4  | H5  | B3  | L5  | Exit |
    +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+```
 
-- ``Entry1`` and ``Entry2`` are converged.
-- ``H1`` and ``H2`` are converged.
-- ``B1`` and ``B2`` are not converged due to ``H4`` which is not
-  convergence-before ``B1``.
-- ``H3`` and ``H4`` are converged.
-- ``H3`` is not converged with ``H5`` due to ``H4`` which is not
-  convergence-before ``H3``.
-- ``L1`` and ``L2`` are converged.
-- ``L3`` and ``L4`` are converged.
-- ``L3`` is not converged with ``L5`` due to ``H5`` which is not
-  convergence-before ``L3``.
+- `Entry1` and `Entry2` are converged.
+- `H1` and `H2` are converged.
+- `B1` and `B2` are not converged due to `H4` which is not
+  convergence-before `B1`.
+- `H3` and `H4` are converged.
+- `H3` is not converged with `H5` due to `H4` which is not
+  convergence-before `H3`.
+- `L1` and `L2` are converged.
+- `L3` and `L4` are converged.
+- `L3` is not converged with `L5` due to `H5` which is not
+  convergence-before `L3`.
 
-.. _convergence-cycle-headers:
+(convergence-cycle-headers)=
 
-Dependence on Cycles Headers
-----------------------------
+### Dependence on Cycles Headers
 
 Contradictions in *convergence-before* are possible only between two
 nodes that are inside some cycle. The dynamic instances of such nodes
 may be interleaved in the same thread, and this interleaving may be
 different for different threads. Cycle headers serve as implicit
 *points of convergence* in the maximal converged-with relation.
-When a thread executes a node ``X`` once and then executes it again,
-it must have followed a closed path in the CFG that includes ``X``.
+When a thread executes a node `X` once and then executes it again,
+it must have followed a closed path in the CFG that includes `X`.
 Such a path must pass through the header of at least one cycle --- the
 smallest cycle that includes the entire closed path. In a given
-thread, two dynamic instances of ``X`` are either separated by the
-execution of at least one cycle header, or ``X`` itself is a cycle
+thread, two dynamic instances of `X` are either separated by the
+execution of at least one cycle header, or `X` itself is a cycle
 header.
 
-Consider a sequence of nested cycles ``C1``, ``C2``, ..., ``Ck`` such
-that ``C1`` is the outermost cycle and ``Ck`` is the innermost cycle,
-with headers ``H1``, ``H2``, ..., ``Hk`` respectively. When a thread
-enters the cycle ``Ck``, any of the following is possible:
-
-1. The thread directly entered cycle ``Ck`` without having executed
-   any of the headers ``H1`` to ``Hk``.
+Consider a sequence of nested cycles `C1`, `C2`, ..., `Ck` such
+that `C1` is the outermost cycle and `Ck` is the innermost cycle,
+with headers `H1`, `H2`, ..., `Hk` respectively. When a thread
+enters the cycle `Ck`, any of the following is possible:
 
+1. The thread directly entered cycle `Ck` without having executed
+   any of the headers `H1` to `Hk`.
 2. The thread executed some or all of the nested headers one or more
    times.
 
 The maximal converged-with relation captures the following intuition
 about cycles:
 
-1. When two threads enter a top-level cycle ``C1``, they execute
-   converged dynamic instances of every node that is a :ref:`child
-   <cycle-parent-block>` of ``C1``.
+1. When two threads enter a top-level cycle `C1`, they execute
+   converged dynamic instances of every node that is a {ref}`child
+   <cycle-parent-block>` of `C1`.
 
-2. When two threads enter a nested cycle ``Ck``, they execute
+2. When two threads enter a nested cycle `Ck`, they execute
    converged dynamic instances of every node that is a child of
-   ``Ck``, until either thread exits ``Ck``, if and only if they
+   `Ck`, until either thread exits `Ck`, if and only if they
    executed converged dynamic instances of the last nested header that
    either thread encountered.
 
-   Note that when a thread exits a nested cycle ``Ck``, it must follow
-   a closed path outside ``Ck`` to reenter it. This requires executing
+   Note that when a thread exits a nested cycle `Ck`, it must follow
+   a closed path outside `Ck` to reenter it. This requires executing
    the header of some outer cycle, as described earlier.
 
-Consider two dynamic instances ``X1`` and ``X2`` produced by threads ``T1``
-and ``T2`` for a node ``X`` that is a child of nested cycle ``Ck``.
-Maximal convergence relates ``X1`` and ``X2`` as follows:
-
-1. If neither thread executed any header from ``H1`` to ``Hk``, then
-   ``X1`` and ``X2`` are converged.
-
-2. Otherwise, if there are no converged dynamic instances ``Q1`` and
-   ``Q2`` of any header ``Q`` from ``H1`` to ``Hk`` (where ``Q`` is
-   possibly the same as ``X``), such that ``Q1`` precedes ``X1`` and
-   ``Q2`` precedes ``X2`` in the respective threads, then ``X1`` and
-   ``X2`` are not converged.
-
-3. Otherwise, consider the pair ``Q1`` and ``Q2`` of converged dynamic
-   instances of a header ``Q`` from ``H1`` to ``Hk`` that occur most
-   recently before ``X1`` and ``X2`` in the respective threads. Then
-   ``X1`` and ``X2`` are converged if and only if there is no dynamic
-   instance of any header from ``H1`` to ``Hk`` that occurs between
-   ``Q1`` and ``X1`` in thread ``T1``, or between ``Q2`` and ``X2`` in
-   thread ``T2``. In other words, ``Q1`` and ``Q2`` represent the last
+Consider two dynamic instances `X1` and `X2` produced by threads `T1`
+and `T2` for a node `X` that is a child of nested cycle `Ck`.
+Maximal convergence relates `X1` and `X2` as follows:
+
+1. If neither thread executed any header from `H1` to `Hk`, then
+   `X1` and `X2` are converged.
+2. Otherwise, if there are no converged dynamic instances `Q1` and
+   `Q2` of any header `Q` from `H1` to `Hk` (where `Q` is
+   possibly the same as `X`), such that `Q1` precedes `X1` and
+   `Q2` precedes `X2` in the respective threads, then `X1` and
+   `X2` are not converged.
+3. Otherwise, consider the pair `Q1` and `Q2` of converged dynamic
+   instances of a header `Q` from `H1` to `Hk` that occur most
+   recently before `X1` and `X2` in the respective threads. Then
+   `X1` and `X2` are converged if and only if there is no dynamic
+   instance of any header from `H1` to `Hk` that occurs between
+   `Q1` and `X1` in thread `T1`, or between `Q2` and `X2` in
+   thread `T2`. In other words, `Q1` and `Q2` represent the last
    point of convergence, with no other header being executed before
-   executing ``X``.
+   executing `X`.
 
 **Example:**
 
-.. figure:: convergence-both-diverged-nested.png
-   :name: convergence-both-diverged-nested
+:::{figure} convergence-both-diverged-nested.png
+:name: convergence-both-diverged-nested
+:::
 
 The above figure shows two nested irreducible cycles with headers
-``R`` and ``S``. The nodes ``Entry`` and ``Q`` have divergent
+`R` and `S`. The nodes `Entry` and `Q` have divergent
 branches. The table below shows the convergence between three threads
 taking different paths through the CFG. Dynamic instances listed in
 the same column are converged.
 
-   .. table::
-      :align: left
-
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-      |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 10   |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-      | Thread1 | Entry | P1  | Q1  | S1  | P3  | Q3  | R1  | S2  | Exit |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-      | Thread2 | Entry | P2  | Q2  |     |     |     | R2  | S3  | Exit |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-      | Thread3 | Entry |     |     |     |     |     | R3  | S4  | Exit |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-
-- ``P2`` and ``P3`` are not converged due to ``S1``
-- ``Q2`` and ``Q3`` are not converged due to ``S1``
-- ``S1`` and ``S3`` are not converged due to ``R2``
-- ``S1`` and ``S4`` are not converged due to ``R3``
-
-Informally, ``T1`` and ``T2`` execute the inner cycle a different
+> ```{eval-rst}
+> .. table::
+>    :align: left
+>
+>    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
+>    |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 10   |
+>    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
+>    | Thread1 | Entry | P1  | Q1  | S1  | P3  | Q3  | R1  | S2  | Exit |
+>    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
+>    | Thread2 | Entry | P2  | Q2  |     |     |     | R2  | S3  | Exit |
+>    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
+>    | Thread3 | Entry |     |     |     |     |     | R3  | S4  | Exit |
+>    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
+> ```
+
+- `P2` and `P3` are not converged due to `S1`
+- `Q2` and `Q3` are not converged due to `S1`
+- `S1` and `S3` are not converged due to `R2`
+- `S1` and `S4` are not converged due to `R3`
+
+Informally, `T1` and `T2` execute the inner cycle a different
 number of times, without executing the header of the outer cycle. All
 threads converge in the outer cycle when they first execute the header
 of the outer cycle.
 
-.. _convergence-uniformity:
+(convergence-uniformity)=
 
-Uniformity
-==========
+## Uniformity
 
 1. The output of two converged dynamic instances is uniform if and
    only if it compares equal for those two dynamic instances.
-2. The output of a static instance ``X`` is uniform *for a given set
+2. The output of a static instance `X` is uniform *for a given set
    of threads* if and only if it is uniform for every pair of
-   converged dynamic instances of ``X`` produced by those threads.
+   converged dynamic instances of `X` produced by those threads.
 
 A non-uniform value is said to be *divergent*.
 
-For a set ``S`` of threads, the uniformity of each output of a static
+For a set `S` of threads, the uniformity of each output of a static
 instance is determined as follows:
 
 1. The semantics of the instruction may specify the output to be
    uniform.
+
 2. Otherwise, the output is divergent if the static instance is not
-   :ref:`m-converged <convergence-m-converged>`.
+   {ref}`m-converged <convergence-m-converged>`.
+
 3. Otherwise, if the static instance is m-converged:
 
    1. If it is a PHI node, its output is uniform if and only
       if for every pair of converged dynamic instances produced by all
-      threads in ``S``:
+      threads in `S`:
 
-      a. Both instances choose the same output from converged
+      1. Both instances choose the same output from converged
          dynamic instances, and,
-      b. That output is uniform for all threads in ``S``.
+      2. That output is uniform for all threads in `S`.
+
    2. Otherwise, the output is uniform if and only if the input
-      operands are uniform for all threads in ``S``.
+      operands are uniform for all threads in `S`.
 
-Divergent Cycle Exits
----------------------
+### Divergent Cycle Exits
 
 When a divergent branch occurs inside a cycle, it is possible that a
 diverged path continues to an exit of the cycle. This is called a
 divergent cycle exit. If the cycle is irreducible, the diverged path
 may re-enter and eventually reach a join within the cycle. Such a join
-should be examined for the :ref:`diverged entry
+should be examined for the {ref}`diverged entry
 <convergence-diverged-entry>` criterion.
 
 Nodes along the diverged path that lie outside the cycle experience
 *temporal divergence*, when two threads executing convergently inside
 the cycle produce uniform values, but exit the cycle along the same
 divergent path after executing the header a different number of times
-(informally, on different iterations of the cycle). For a node ``N``
+(informally, on different iterations of the cycle). For a node `N`
 inside the cycle the outputs may be uniform for the two threads, but
-any use ``U`` outside the cycle receives a value from non-converged
-dynamic instances of ``N``. An output of ``U`` may be divergent,
+any use `U` outside the cycle receives a value from non-converged
+dynamic instances of `N`. An output of `U` may be divergent,
 depending on the semantics of the instruction.
 
-.. _uniformity-analysis:
+(uniformity-analysis)=
 
-Static Uniformity Analysis
-==========================
+## Static Uniformity Analysis
 
 Irreducible control flow results in different cycle hierarchies
 depending on the choice of headers during depth-first traversal. As a
@@ -442,46 +443,45 @@ nodes in irreducible cycles, and any uniformity analysis is limited to
 those static instances whose convergence is independent of the cycle
 hierarchy:
 
-.. _convergence-m-converged:
-
-  **m-converged static instances:**
-
-  A static instance ``X`` is *m-converged* for a given CFG if and only
-  if the maximal converged-with relation for its dynamic instances is
-  the same in every cycle hierarchy that can be constructed for that CFG.
-
-  .. note::
-
-   In other words, two dynamic instances ``X1`` and ``X2`` of an
-   m-converged static instance ``X`` are converged in some cycle
-   hierarchy if and only if they are also converged in every other
-   cycle hierarchy for the same CFG.
-
-   As noted earlier, for brevity, we restrict the term *converged* to
-   mean "related under the maximal converged-with relation for a given
-   cycle hierarchy".
-
-
-Each node ``X`` in a given CFG is reported to be m-converged if and
-only if every cycle that contains ``X`` satisfies the following necessary
+(convergence-m-converged)=
+
+> **m-converged static instances:**
+>
+> A static instance `X` is *m-converged* for a given CFG if and only
+> if the maximal converged-with relation for its dynamic instances is
+> the same in every cycle hierarchy that can be constructed for that CFG.
+>
+> :::{note}
+> In other words, two dynamic instances `X1` and `X2` of an
+> m-converged static instance `X` are converged in some cycle
+> hierarchy if and only if they are also converged in every other
+> cycle hierarchy for the same CFG.
+>
+> As noted earlier, for brevity, we restrict the term *converged* to
+> mean "related under the maximal converged-with relation for a given
+> cycle hierarchy".
+> :::
+
+Each node `X` in a given CFG is reported to be m-converged if and
+only if every cycle that contains `X` satisfies the following necessary
 conditions:
 
-  1. Every divergent branch inside the cycle satisfies the
-     :ref:`diverged entry criterion<convergence-diverged-entry>`, and,
-  2. There are no :ref:`diverged paths reaching the
-     cycle<convergence-diverged-outside>` from a divergent branch
-     outside it.
+> 1. Every divergent branch inside the cycle satisfies the
+>    {ref}`diverged entry criterion<convergence-diverged-entry>`, and,
+> 2. There are no {ref}`diverged paths reaching the
+>    cycle<convergence-diverged-outside>` from a divergent branch
+>    outside it.
 
-.. note::
-
-   A reducible cycle :ref:`trivially satisfies
-   <convergence-reducible-cycle>` the above conditions. In particular,
-   if the whole CFG is reducible, then all nodes in the CFG are
-   m-converged.
+:::{note}
+A reducible cycle {ref}`trivially satisfies
+<convergence-reducible-cycle>` the above conditions. In particular,
+if the whole CFG is reducible, then all nodes in the CFG are
+m-converged.
+:::
 
 The uniformity of each output of a static instance
 is determined using the criteria
-:ref:`described earlier <convergence-uniformity>`. The discovery of
+{ref}`described earlier <convergence-uniformity>`. The discovery of
 divergent outputs may cause their uses (including branches) to also
 become divergent. The analysis propagates this divergence until a
 fixed point is reached.
@@ -489,8 +489,8 @@ fixed point is reached.
 The convergence inferred using these criteria is a safe subset of the
 maximal converged-with relation for any cycle hierarchy. In
 particular, it is sufficient to determine if a static instance is
-m-converged for a given cycle hierarchy ``T``, even if that fact is
-not detected when examining some other cycle hierarchy ``T'``.
+m-converged for a given cycle hierarchy `T`, even if that fact is
+not detected when examining some other cycle hierarchy `T'`.
 
 This property allows compiler transforms to use the uniformity
 analysis without being affected by DFS choices made in the underlying
@@ -505,25 +505,24 @@ converged-with relations. This also means that a value that was
 previously uniform can become divergent after such a transform.
 Uniformity has to be recomputed after such transforms.
 
-Divergent Branch inside a Cycle
--------------------------------
+### Divergent Branch inside a Cycle
 
-.. figure:: convergence-divergent-inside.png
-   :name: convergence-divergent-inside
+:::{figure} convergence-divergent-inside.png
+:name: convergence-divergent-inside
+:::
 
-The above figure shows a divergent branch ``Q`` inside an irreducible
-cyclic region. When two threads diverge at ``Q``, the convergence of
+The above figure shows a divergent branch `Q` inside an irreducible
+cyclic region. When two threads diverge at `Q`, the convergence of
 dynamic instances within the cyclic region depends on the cycle
 hierarchy chosen:
 
-1. In an implementation that detects a single cycle ``C`` with header
-   ``P``, convergence inside the cycle is determined by ``P``.
-
+1. In an implementation that detects a single cycle `C` with header
+   `P`, convergence inside the cycle is determined by `P`.
 2. In an implementation that detects two nested cycles with headers
-   ``R`` and ``S``, convergence inside those cycles is determined by
+   `R` and `S`, convergence inside those cycles is determined by
    their respective headers.
 
-.. _convergence-diverged-entry:
+(convergence-diverged-entry)=
 
 A conservative approach would be to simply report all nodes inside
 irreducible cycles as having divergent outputs. But it is desirable to
@@ -532,34 +531,36 @@ uniformity. This section describes one such pattern of nodes derived
 from *closed paths*, which are a property of the CFG and do not depend
 on the cycle hierarchy.
 
-  **Diverged Entry Criterion:**
-
-  The dynamic instances of all the nodes in a closed path ``P`` are
-  m-converged only if for every divergent branch ``B`` and its
-  join node ``J`` that lie on ``P``, there is no entry to ``P`` which
-  lies on a diverged path from ``B`` to ``J``.
-
-.. figure:: convergence-closed-path.png
-   :name: convergence-closed-path
-
-Consider the closed path ``P -> Q -> R -> S`` in the above figure.
-``P`` and ``R`` are :ref:`entries to the closed
-path<cycle-closed-path>`. ``Q`` is a divergent branch and ``S`` is a
-join for that branch, with diverged paths ``Q -> R -> S`` and ``Q ->
-S``.
-
-- If a diverged entry ``R`` exists, then in some cycle hierarchy,
-  ``R`` is the header of the smallest cycle ``C`` containing the
-  closed path and a :ref:`child cycle<cycle-definition>` ``C'``
-  exists in the set ``C - R``, containing both branch ``Q`` and join
-  ``S``. When threads diverge at ``Q``, one subset ``M`` continues
-  inside cycle ``C'``, while the complement ``N`` exits ``C'`` and
-  reaches ``R``. Dynamic instances of ``S`` executed by threads in set
-  ``M`` are not converged with those executed in set ``N`` due to the
-  presence of ``R``. Informally, threads that diverge at ``Q``
-  reconverge in the same iteration of the outer cycle ``C``, but they
-  may have executed the inner cycle ``C'`` differently.
-
+> **Diverged Entry Criterion:**
+>
+> The dynamic instances of all the nodes in a closed path `P` are
+> m-converged only if for every divergent branch `B` and its
+> join node `J` that lie on `P`, there is no entry to `P` which
+> lies on a diverged path from `B` to `J`.
+
+:::{figure} convergence-closed-path.png
+:name: convergence-closed-path
+:::
+
+Consider the closed path `P -> Q -> R -> S` in the above figure.
+`P` and `R` are {ref}`entries to the closed
+path<cycle-closed-path>`. `Q` is a divergent branch and `S` is a
+join for that branch, with diverged paths `Q -> R -> S` and `Q ->
+S`.
+
+- If a diverged entry `R` exists, then in some cycle hierarchy,
+  `R` is the header of the smallest cycle `C` containing the
+  closed path and a {ref}`child cycle<cycle-definition>` `C'`
+  exists in the set `C - R`, containing both branch `Q` and join
+  `S`. When threads diverge at `Q`, one subset `M` continues
+  inside cycle `C'`, while the complement `N` exits `C'` and
+  reaches `R`. Dynamic instances of `S` executed by threads in set
+  `M` are not converged with those executed in set `N` due to the
+  presence of `R`. Informally, threads that diverge at `Q`
+  reconverge in the same iteration of the outer cycle `C`, but they
+  may have executed the inner cycle `C'` differently.
+
+  ```{eval-rst}
   .. table::
      :align: left
 
@@ -570,18 +571,18 @@ S``.
      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
      | Thread2 | Entry | P2  | Q2  | S2  | P4  | Q4  | R2  | S4  |     |     | Exit |
      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+  ```
 
-  In the table above, ``S2`` is not converged with ``S1`` due to ``R1``.
+  In the table above, `S2` is not converged with `S1` due to `R1`.
 
-|
-
-- If ``R`` does not exist, or if any node other than ``R`` is the
-  header of ``C``, then no such child cycle ``C'`` is detected.
-  Threads that diverge at ``Q`` execute converged dynamic instances of
-  ``S`` since they do not encounter the cycle header on any path from
-  ``Q`` to ``S``. Informally, threads that diverge at ``Q``
-  reconverge at ``S`` in the same iteration of ``C``.
+* If `R` does not exist, or if any node other than `R` is the
+  header of `C`, then no such child cycle `C'` is detected.
+  Threads that diverge at `Q` execute converged dynamic instances of
+  `S` since they do not encounter the cycle header on any path from
+  `Q` to `S`. Informally, threads that diverge at `Q`
+  reconverge at `S` in the same iteration of `C`.
 
+  ```{eval-rst}
   .. table::
      :align: left
 
@@ -592,122 +593,120 @@ S``.
      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+------+
      | Thread2 | Entry | P2  | Q2  |     | S2  | P4  | Q4  | R2  | S4  | Exit |
      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+------+
-
-|
-
-  .. note::
-
-     In general, the cycle ``C`` in the above statements is not
-     expected to be the same cycle for different headers. Cycles and
-     their headers are tightly coupled; for different headers in the
-     same outermost cycle, the child cycles detected may be different.
-     The property relevant to the above examples is that for every
-     closed path, there is a cycle ``C`` that contains the path and
-     whose header is on that path.
+  ```
+
+> :::{note}
+> In general, the cycle `C` in the above statements is not
+> expected to be the same cycle for different headers. Cycles and
+> their headers are tightly coupled; for different headers in the
+> same outermost cycle, the child cycles detected may be different.
+> The property relevant to the above examples is that for every
+> closed path, there is a cycle `C` that contains the path and
+> whose header is on that path.
+> :::
 
 The diverged entry criterion must be checked for every closed path
-passing through a divergent branch ``B`` and its join ``J``. Since
-:ref:`every closed path passes through the header of some
+passing through a divergent branch `B` and its join `J`. Since
+{ref}`every closed path passes through the header of some
 cycle<cycle-closed-path-header>`, this amounts to checking every cycle
-``C`` that contains ``B`` and ``J``. When the header of ``C``
-dominates the join ``J``, there can be no entry to any path from the
-header to ``J``, which includes any diverged path from ``B`` to ``J``.
+`C` that contains `B` and `J`. When the header of `C`
+dominates the join `J`, there can be no entry to any path from the
+header to `J`, which includes any diverged path from `B` to `J`.
 This is also true for any closed paths passing through the header of
-an outer cycle that contains ``C``.
+an outer cycle that contains `C`.
 
 Thus, the diverged entry criterion can be conservatively simplified
 as follows:
 
-  For a divergent branch ``B`` and its join node ``J``, the nodes in a
-  cycle ``C`` that contains both ``B`` and ``J`` are m-converged only
-  if:
-
-  - ``B`` strictly dominates ``J``, or,
-  - The header ``H`` of ``C`` strictly dominates ``J``, or,
-  - Recursively, there is cycle ``C'`` inside ``C`` that satisfies the
-    same condition.
+> For a divergent branch `B` and its join node `J`, the nodes in a
+> cycle `C` that contains both `B` and `J` are m-converged only
+> if:
+>
+> - `B` strictly dominates `J`, or,
+> - The header `H` of `C` strictly dominates `J`, or,
+> - Recursively, there is cycle `C'` inside `C` that satisfies the
+>   same condition.
 
-When ``J`` is the same as ``H`` or ``B``, the trivial dominance is
+When `J` is the same as `H` or `B`, the trivial dominance is
 insufficient to make any statement about entries to diverged paths.
 
-.. _convergence-diverged-outside:
+(convergence-diverged-outside)=
 
-Diverged Paths reaching a Cycle
--------------------------------
+### Diverged Paths reaching a Cycle
 
-.. figure:: convergence-divergent-outside.png
-   :name: convergence-divergent-outside
+:::{figure} convergence-divergent-outside.png
+:name: convergence-divergent-outside
+:::
 
 The figure shows two cycle hierarchies with a divergent branch in
-``Entry`` instead of ``Q``. For two threads that enter the closed path
-``P -> Q -> R -> S`` at ``P`` and ``R`` respectively, the convergence
-of dynamic instances generated along the path depends on whether ``P``
-or ``R`` is the header.
-
--  Convergence when ``P`` is the header.
-
-   .. table::
-      :align: left
+`Entry` instead of `Q`. For two threads that enter the closed path
+`P -> Q -> R -> S` at `P` and `R` respectively, the convergence
+of dynamic instances generated along the path depends on whether `P`
+or `R` is the header.
 
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-      |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11  | 12  | 13   |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-      | Thread1 | Entry |     |     |     | P1  | Q1  | R1  | S1  | P3  | Q3  |     | S3  | Exit |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-      | Thread2 | Entry |     | R2  | S2  | P2  | Q2  |     | S2  | P4  | Q4  | R3  | S4  | Exit |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+- Convergence when `P` is the header.
 
-   |
+  ```{eval-rst}
+  .. table::
+     :align: left
 
--  Convergence when ``R`` is the header.
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+     |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11  | 12  | 13   |
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+     | Thread1 | Entry |     |     |     | P1  | Q1  | R1  | S1  | P3  | Q3  |     | S3  | Exit |
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+     | Thread2 | Entry |     | R2  | S2  | P2  | Q2  |     | S2  | P4  | Q4  | R3  | S4  | Exit |
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+  ```
 
-   .. table::
-      :align: left
+- Convergence when `R` is the header.
 
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-      |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11  | 12   |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-      | Thread1 | Entry |     | P1  | Q1  | R1  | S1  | P3  | Q3  | S3  |     |     | Exit |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-      | Thread2 | Entry |     |     |     | R2  | S2  | P2  | Q2  | S2  | P4  | ... | Exit |
-      +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+  ```{eval-rst}
+  .. table::
+     :align: left
 
-   |
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+     |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11  | 12   |
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+     | Thread1 | Entry |     | P1  | Q1  | R1  | S1  | P3  | Q3  | S3  |     |     | Exit |
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+     | Thread2 | Entry |     |     |     | R2  | S2  | P2  | Q2  | S2  | P4  | ... | Exit |
+     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
+  ```
 
 Thus, when diverged paths reach different entries of an irreducible
 cycle from outside the cycle, the static analysis conservatively
 reports every node in the cycle as not m-converged.
 
-.. _convergence-reducible-cycle:
+(convergence-reducible-cycle)=
 
-Reducible Cycle
----------------
+### Reducible Cycle
 
-If ``C`` is a reducible cycle with header ``H``, then in any DFS,
-``H`` :ref:`must be the header of some cycle<cycle-reducible-headers>`
-``C'`` that contains ``C``. Independent of the DFS, there is no entry
-to the subgraph ``C`` other than ``H`` itself. Thus, we have the
+If `C` is a reducible cycle with header `H`, then in any DFS,
+`H` {ref}`must be the header of some cycle<cycle-reducible-headers>`
+`C'` that contains `C`. Independent of the DFS, there is no entry
+to the subgraph `C` other than `H` itself. Thus, we have the
 following:
 
 1. The diverged entry criterion is trivially satisfied for a divergent
-   branch and its join, where both are inside subgraph ``C``.
-2. When diverged paths reach the subgraph ``C`` from outside, their
-   convergence is always determined by the same header ``H``.
+   branch and its join, where both are inside subgraph `C`.
+2. When diverged paths reach the subgraph `C` from outside, their
+   convergence is always determined by the same header `H`.
 
-Clearly, this can be determined only in a cycle hierarchy ``T`` where
-``C`` is detected as a reducible cycle. No such conclusion can be made
-in a different cycle hierarchy ``T'`` where ``C`` is part of a larger
-cycle ``C'`` with the same header, but this does not contradict the
-conclusion in ``T``.
+Clearly, this can be determined only in a cycle hierarchy `T` where
+`C` is detected as a reducible cycle. No such conclusion can be made
+in a different cycle hierarchy `T'` where `C` is part of a larger
+cycle `C'` with the same header, but this does not contradict the
+conclusion in `T`.
 
-Controlled Convergence
-======================
+## Controlled Convergence
 
-:ref:`Convergence control tokens <dynamic_instances_and_convergence_tokens>`
+{ref}`Convergence control tokens <dynamic_instances_and_convergence_tokens>`
 provide an explicit semantics for determining which threads are converged at a
 given point in the program. The impact of this is incorporated in a
-:ref:`controlled maximal converged-with <controlled_maximal_converged_with>`
-relation over dynamic instances and a :ref:`controlled m-converged
-<controlled_m_converged>` property of static instances. The :ref:`uniformity
+{ref}`controlled maximal converged-with <controlled_maximal_converged_with>`
+relation over dynamic instances and a {ref}`controlled m-converged
+<controlled_m_converged>` property of static instances. The {ref}`uniformity
 analysis <uniformity-analysis>` implemented in LLVM includes this for targets
 that support convergence control tokens.
+
diff --git a/llvm/docs/ConvergentOperations.md b/llvm/docs/ConvergentOperations.md
index d29711804c90e..1605ab82da152 100644
--- a/llvm/docs/ConvergentOperations.md
+++ b/llvm/docs/ConvergentOperations.md
@@ -1,38 +1,34 @@
-==============================
-Convergent Operation Semantics
-==============================
+# Convergent Operation Semantics
 
-
-Overview
-========
+## Overview
 
 Some parallel execution environments execute threads in groups that allow
 efficient communication within the group using special primitives called
 *convergent* operations. The outcome of a convergent operation is sensitive to
 the set of threads that executes it "together", i.e., convergently. When control
-flow :ref:`diverges <convergence-and-uniformity>`, i.e., threads of the same
+flow {ref}`diverges <convergence-and-uniformity>`, i.e., threads of the same
 group follow different
 paths through the CFG, not all threads of the group may be available to
 participate in this communication. This is the defining characteristic that
 distinguishes convergent operations from other inter-thread communication:
 
-  A convergent operation involves inter-thread communication or synchronization
-  that occurs outside of the memory model, where the set of threads which
-  participate in communication is implicitly affected by control flow.
+> A convergent operation involves inter-thread communication or synchronization
+> that occurs outside of the memory model, where the set of threads which
+> participate in communication is implicitly affected by control flow.
 
 For example, in the following GPU compute kernel, communication during the
 convergent operation is expected to occur precisely among those threads of an
 implementation-defined execution scope (such as workgroup or subgroup) for
-which ``condition`` is true:
-
-.. code-block:: c++
+which `condition` is true:
 
-  void example_kernel() {
-      ...
-      if (condition)
-          convergent_operation();
-      ...
-  }
+```c++
+void example_kernel() {
+    ...
+    if (condition)
+        convergent_operation();
+    ...
+}
+```
 
 In structured programming languages, there is often an intuitive and
 unambiguous way of determining the threads that are expected to communicate.
@@ -44,48 +40,45 @@ of communicating threads for convergent operations.
 The definitions in this document leave many details open, such as how groups of
 threads are formed in the first place. It focuses on the questions that are
 relevant for deciding the correctness of generic program transforms and
-convergence-related analyses such as :ref:`uniformity analysis
+convergence-related analyses such as {ref}`uniformity analysis
 <convergence-and-uniformity>`.
 
-.. _convergent_operations:
+(convergent-operations)=
 
-Convergent Operations
-=====================
+## Convergent Operations
 
 In LLVM IR, the only way to communicate between threads as described
 above is by calling target-defined convergent intrinsics. Hence, only
-a call-site in LLVM IR (a :ref:`call <i_call>`, :ref:`invoke
-<i_invoke>`, or :ref:`callbr <i_callbr>` instruction) can result in a
+a call-site in LLVM IR (a {ref}`call <i_call>`, {ref}`invoke
+<i_invoke>`, or {ref}`callbr <i_callbr>` instruction) can result in a
 convergent operation.
 
 A function in LLVM IR is said to be *convergent* if it has the
-:ref:`convergent <attr_convergent>` attribute.
+{ref}`convergent <attr_convergent>` attribute.
 
 A call-site in LLVM IR is said to be *convergent* if it is a direct
-call to a convergent function or it has the :ref:`convergent
-<attr_convergent>` attribute or a :ref:`convergencectrl operand bundle
+call to a convergent function or it has the {ref}`convergent
+<attr_convergent>` attribute or a {ref}`convergencectrl operand bundle
 <convergencectrl>`.
 
 Informational notes:
 
-  A function may have to be treated as convergent if that function, or
-  transitively, any function called from it, contains a convergent call-site. A
-  frontend generating the ``convergent`` attribute should take this into account
-  when emitting functions and function calls. But this is not always the case:
-
-  A non-convergent function may contain convergent operations; such operations
-  do not directly depend on the set of threads that enter the function as a
-  single communicating group. Instead, these operations depend on an
-  implementation-defined subset of threads within the body of the function, as
-  shown in :ref:`opportunistic_convergence`.
+> A function may have to be treated as convergent if that function, or
+> transitively, any function called from it, contains a convergent call-site. A
+> frontend generating the `convergent` attribute should take this into account
+> when emitting functions and function calls. But this is not always the case:
+>
+> A non-convergent function may contain convergent operations; such operations
+> do not directly depend on the set of threads that enter the function as a
+> single communicating group. Instead, these operations depend on an
+> implementation-defined subset of threads within the body of the function, as
+> shown in {ref}`opportunistic_convergence`.
 
-Examples of Convergent Operations
-========================================
+## Examples of Convergent Operations
 
 (This section is informative.)
 
-Texture sampling in a pixel shader
-----------------------------------
+### Texture sampling in a pixel shader
 
 The following stylized pixel shader samples a texture at a given set of
 coordinates, using the builtin function `textureSample`. Texture sampling
@@ -94,16 +87,16 @@ detail (mipmap level) of the sample. They are commonly approximated by taking
 the difference between neighboring pixels, which are computed by different
 threads in the same group:
 
-.. code-block:: c++
-
-  void example_shader() {
-    ...
-    color = textureSample(texture, coordinates);
-    if (condition) {
-      use(color);
-    }
-    ...
+```c++
+void example_shader() {
+  ...
+  color = textureSample(texture, coordinates);
+  if (condition) {
+    use(color);
   }
+  ...
+}
+```
 
 From a purely single-threaded perspective, sinking the `textureSample` into
 the if-statement appears legal. However, if the condition is false for some
@@ -115,225 +108,220 @@ an undefined value.
 That is, the `textureSample` operation fits our definition of a convergent
 operation:
 
- 1. It communicates with a set of threads that implicitly depends on control
-    flow.
- 2. Correctness depends on this set of threads.
+> 1. It communicates with a set of threads that implicitly depends on control
+>    flow.
+> 2. Correctness depends on this set of threads.
 
 The compiler frontend can emit IR that expresses the convergence constraints as
 follows:
 
-.. code-block:: llvm
+```llvm
+define void @example_shader() convergent {
+  %entry = call token @llvm.experimental.convergence.entry()
+  ...
+  %color = call T @textureSample(U %texture, V %coordinates) [ "convergencectrl"(token %entry) ]
+  br i1 %condition, label %then, label %end
 
-  define void @example_shader() convergent {
-    %entry = call token @llvm.experimental.convergence.entry()
-    ...
-    %color = call T @textureSample(U %texture, V %coordinates) [ "convergencectrl"(token %entry) ]
-    br i1 %condition, label %then, label %end
-
-  then:
-    call void @use(T %color)
-    br label %end
+then:
+  call void @use(T %color)
+  br label %end
 
-  end:
-    ret void
-  }
+end:
+  ret void
+}
+```
 
-The :ref:`llvm.experimental.convergence.entry <llvm.experimental.convergence.entry>`
-intrinsic is itself ``convergent``, and we expect it to communicate at least
+The {ref}`llvm.experimental.convergence.entry <llvm.experimental.convergence.entry>`
+intrinsic is itself `convergent`, and we expect it to communicate at least
 among all threads of the same "quad" -- a group of 2x2 pixels that are
 evaluated together for the purpose of approximating screen-space derivatives.
 This fact is not part of the generic LLVM IR semantics; it would have to be
 defined somewhere else, for example as part of target-specific ABI definitions
 and/or in reference to some relevant API specs.
 
-Since the ``@textureSample`` call then uses the token produced by the entry
-intrinsic in its ``convergencectrl`` bundle, and has no additional control
+Since the `@textureSample` call then uses the token produced by the entry
+intrinsic in its `convergencectrl` bundle, and has no additional control
 dependencies, it must communicate among the same set of threads. This indicates
-to generic program transforms that sinking the ``@textureSample`` call is
+to generic program transforms that sinking the `@textureSample` call is
 forbidden. (A program transform can still sink the call if it can prove somehow,
 e.g. by leaning on target-specific callbacks that can analyze the program with
-additional knowledge, that ``%condition`` is always uniform across the threads
-referenced by the *convergence token* ``%entry``.)
+additional knowledge, that `%condition` is always uniform across the threads
+referenced by the *convergence token* `%entry`.)
 
-.. _convergence_example_reductions:
+(convergence-example-reductions)=
 
-Reductions inside divergent control flow
-----------------------------------------
+### Reductions inside divergent control flow
 
 The following example shows that merging common code of branches can be
 incorrect in the face of convergent operations:
 
-.. code-block:: c++
-
-  void example_kernel() {
-    delta = ...
-    if (delta > 0) {
-      total_gains = subgroupAdd(delta);
-      ...
-    } else {
-      total_losses = subgroupAdd(delta);
-      ...
-    }
+```c++
+void example_kernel() {
+  delta = ...
+  if (delta > 0) {
+    total_gains = subgroupAdd(delta);
+    ...
+  } else {
+    total_losses = subgroupAdd(delta);
+    ...
   }
+}
+```
 
-The ``subgroupAdd`` computing the ``total_gains`` will be executed by the
-subset of threads with positive ``delta`` in a subgroup (wave), and so will sum
-up all the ``delta`` values of those threads; and similarly for the
-``subgroupAdd`` that computes the ``total_losses``.
+The `subgroupAdd` computing the `total_gains` will be executed by the
+subset of threads with positive `delta` in a subgroup (wave), and so will sum
+up all the `delta` values of those threads; and similarly for the
+`subgroupAdd` that computes the `total_losses`.
 
-If we were to hoist and merge the ``subgroupAdd`` above the if-statement, it
-would sum up the ``delta`` across *all* threads instead.
+If we were to hoist and merge the `subgroupAdd` above the if-statement, it
+would sum up the `delta` across *all* threads instead.
 
 The compiler frontend can emit IR that expresses the convergence constraints
 as follows:
 
-.. code-block:: llvm
-
-  define void @example_kernel() convergent {
-    %entry = call token @llvm.experimental.convergence.entry()
-    %delta = ...
-    %cc = icmp sgt i32 %delta, 0
-    br i1 %cc, label %then, label %else
+```llvm
+define void @example_kernel() convergent {
+  %entry = call token @llvm.experimental.convergence.entry()
+  %delta = ...
+  %cc = icmp sgt i32 %delta, 0
+  br i1 %cc, label %then, label %else
 
-  then:
-    %total_gains = call i32 @subgroupAdd(i32 %delta) [ "convergencectrl"(token %entry) ]
-    ...
-    br label %end
+then:
+  %total_gains = call i32 @subgroupAdd(i32 %delta) [ "convergencectrl"(token %entry) ]
+  ...
+  br label %end
 
-  else:
-    %total_losses = call i32 @subgroupAdd(i32 %delta) [ "convergencectrl"(token %entry) ]
-    ...
-    br label %end
+else:
+  %total_losses = call i32 @subgroupAdd(i32 %delta) [ "convergencectrl"(token %entry) ]
+  ...
+  br label %end
 
-  end:
-    ...
-  }
+end:
+  ...
+}
+```
 
 The entry intrinsic behaves like in the previous example: assuming that
-``@example_kernel`` is an OpenCL kernel (as hinted at by the "subgroup"
+`@example_kernel` is an OpenCL kernel (as hinted at by the "subgroup"
 terminology), we expect it to communicate among all threads within the
 "subgroup". This typically maps to a SIMD vector on GPU hardware.
 
-The calls to ``@subgroupAdd`` use the token produced by the entry intrinsic,
+The calls to `@subgroupAdd` use the token produced by the entry intrinsic,
 but they also have an additional control dependency. According to the rules
 defined in this document, they only communicate among the subset of threads
 that actually end up executing the respective (static) call site.
 
 Hoisting them would remove the control dependency and cause them to communicate
 among the full set of threads that the entry intrinsic communicated with.
-Again, hoisting is allowed if it can be proven that ``%cc`` is always uniform
-among the relevant set of threads: in that case, the ``@subgroupAdd`` already
+Again, hoisting is allowed if it can be proven that `%cc` is always uniform
+among the relevant set of threads: in that case, the `@subgroupAdd` already
 communicates among the full set of threads in the original program.
 
-Motivating Examples of Convergence Control
-==========================================
+## Motivating Examples of Convergence Control
 
 (This section is informative.)
 
-Unstructured control flow
--------------------------
+### Unstructured control flow
 
 Consider an example of how jump threading removes structure in a way that can
 make semantics non-obvious without the convergence intrinsics described in this
 document:
 
-.. code-block:: llvm
-
-  void example_original() {
-  entry:
-      ...
-      br i1 %cond1, label %then1, label %mid
+```llvm
+void example_original() {
+entry:
+    ...
+    br i1 %cond1, label %then1, label %mid
 
-  then1:
-      ...
-      %cond2 = ...
-      br label %mid
+then1:
+    ...
+    %cond2 = ...
+    br label %mid
 
-  mid:
-      %flag = phi i1 [ true, %entry ], [ %cond2, %then1 ]
-      br i1 %flag, label %then2, label %end
+mid:
+    %flag = phi i1 [ true, %entry ], [ %cond2, %then1 ]
+    br i1 %flag, label %then2, label %end
 
-  then2:
-      ...
-      call void @subgroupControlBarrier()
-      ...
-      br label %end
+then2:
+    ...
+    call void @subgroupControlBarrier()
+    ...
+    br label %end
 
-  end:
-  }
+end:
+}
 
-  void example_jumpthreaded() {
-  entry:
-      ...
-      br i1 %cond1, label %then1, label %then2
+void example_jumpthreaded() {
+entry:
+    ...
+    br i1 %cond1, label %then1, label %then2
 
-  then1:
-      ...
-      %cond2 = ...
-      br i1 %cond2, label %then2, label %end
+then1:
+    ...
+    %cond2 = ...
+    br i1 %cond2, label %then2, label %end
 
-  then2:
-      ...
-      call void @subgroupControlBarrier()
-      ...
-      br label %end
+then2:
+    ...
+    call void @subgroupControlBarrier()
+    ...
+    br label %end
 
-  end:
-  }
+end:
+}
+```
 
 Is the control barrier guaranteed to synchronize among the same set of threads
 in both cases? Different implementations in the literature may give different
 answers to this question:
 
-* In an implementation that reconverges at post-dominators, threads reconverge
-  at ``mid`` in the first version, so that all threads (within a subgroup/wave)
+- In an implementation that reconverges at post-dominators, threads reconverge
+  at `mid` in the first version, so that all threads (within a subgroup/wave)
   that execute the control barrier do so together. In the second version,
   threads that reach the control barrier via different paths synchronize
-  separately: the first (and only) post-dominator is ``end``, so threads do not
+  separately: the first (and only) post-dominator is `end`, so threads do not
   reconverge before then.
-
-* An implementation that sorts basic blocks topologically and ensures maximal
+- An implementation that sorts basic blocks topologically and ensures maximal
   reconvergence for each basic block would behave the same way in both
   versions.
 
 We generally take the stance that reconvergence in acyclic control flow must
 be maximal. The compiler frontend could augment the original code as follows:
 
-.. code-block:: llvm
+```llvm
+define void @example_original() convergent {
+entry:
+  %entry = call token @llvm.experimental.convergence.entry()
+  ...
+  br i1 %cond1, label %then1, label %mid
 
-  define void @example_original() convergent {
-  entry:
-    %entry = call token @llvm.experimental.convergence.entry()
-    ...
-    br i1 %cond1, label %then1, label %mid
+then1:
+  ...
+  %cond2 = ...
+  br label %mid
 
-  then1:
-    ...
-    %cond2 = ...
-    br label %mid
+mid:
+  %flag = phi i1 [ true, %entry ], [ %cond2, %then1 ]
+  br i1 %flag, label %then2, label %end
 
-  mid:
-    %flag = phi i1 [ true, %entry ], [ %cond2, %then1 ]
-    br i1 %flag, label %then2, label %end
+then2:
+  ...
+  call void @subgroupControlBarrier() [ "convergencectrl"(token %entry) ]
+  ...
+  br label %end
 
-  then2:
-    ...
-    call void @subgroupControlBarrier() [ "convergencectrl"(token %entry) ]
-    ...
-    br label %end
-
-  end:
-  }
+end:
+}
+```
 
 If S is the set of threads that the entry intrinsic communicated with, then
-the ``@subgroupControlBarrier`` call communicates with the subset of S that
+the `@subgroupControlBarrier` call communicates with the subset of S that
 actually reaches the call site. This set of threads doesn't change after
 jump-threading, so the answer to the question posed above remains the same.
 
-.. _opportunistic_convergence:
+(opportunistic-convergence)=
 
-Opportunistic convergent operations
------------------------------------
+### Opportunistic convergent operations
 
 Some programs have local regions of code that contain a sequence of convergent
 operations where the code does not care about the exact set of threads with
@@ -342,13 +330,13 @@ operations within the sequence. (If a subset of the convergent operations in the
 sequence have additional, non-uniform control dependencies, then this is not
 possible. However, the code may still require that the sets of threads are
 logically consistent with the conditions of those control dependencies.) In this
-case, :ref:`llvm.experimental.convergence.anchor
+case, {ref}`llvm.experimental.convergence.anchor
 <llvm.experimental.convergence.anchor>` can be used to express the desired
 semantics.
 
 The following example function could be part of a hypothetical "append buffer"
 implementation, where threads conditionally write fixed-sized records
-contiguously into a global buffer. The function ``@reserveSpaceInBuffer``
+contiguously into a global buffer. The function `@reserveSpaceInBuffer`
 returns the index into the buffer at which the calling thread should store its
 data.
 
@@ -362,53 +350,53 @@ operand to the atomic operation, and then later broadcasts the result of the
 atomic operation to all threads of the group, so that each thread can compute
 its individual position in the buffer:
 
-.. code-block:: llvm
-
-  define i32 @reserveSpaceInBuffer() {    ; NOTE: _not_ a convergent function!
-  entry:
-    %anchor = call token @llvm.experimental.convergence.anchor()
+```llvm
+define i32 @reserveSpaceInBuffer() {    ; NOTE: _not_ a convergent function!
+entry:
+  %anchor = call token @llvm.experimental.convergence.anchor()
 
-    %ballot = call i64 @subgroupBallot(i1 true) [ "convergencectrl"(token %anchor) ]
-    %numThreads.p = call i64 @llvm.ctpop.i64(i64 %ballot)
-    %numThreads = trunc i64 %numThreads.p to i32
+  %ballot = call i64 @subgroupBallot(i1 true) [ "convergencectrl"(token %anchor) ]
+  %numThreads.p = call i64 @llvm.ctpop.i64(i64 %ballot)
+  %numThreads = trunc i64 %numThreads.p to i32
 
-    %absoluteThreadIdx = call i32 @getSubgroupLocalInvocationId()
-    %absoluteThreadIdx.ext = zext i32 %absoluteThreadIdx to i64
-    %mask.p = shl i64 1, %absoluteThreadIdx.ext
-    %mask = sub i64 %mask.p, 1
+  %absoluteThreadIdx = call i32 @getSubgroupLocalInvocationId()
+  %absoluteThreadIdx.ext = zext i32 %absoluteThreadIdx to i64
+  %mask.p = shl i64 1, %absoluteThreadIdx.ext
+  %mask = sub i64 %mask.p, 1
 
-    %maskedBallot = and i64 %ballot, %mask
-    %relativeThreadIdx.p = call i64 @llvm.ctpop.i64(i64 %maskedBallot)
-    %relativeThreadIdx = trunc i64 %relativeThreadIdx.p to i32
+  %maskedBallot = and i64 %ballot, %mask
+  %relativeThreadIdx.p = call i64 @llvm.ctpop.i64(i64 %maskedBallot)
+  %relativeThreadIdx = trunc i64 %relativeThreadIdx.p to i32
 
-    %isFirstThread = icmp eq i32 %relativeThreadIdx, 0
-    br i1 %isFirstThread, label %then, label %end
+  %isFirstThread = icmp eq i32 %relativeThreadIdx, 0
+  br i1 %isFirstThread, label %then, label %end
 
-  then:
-    %baseOffset.1 = atomicrmw add ptr @bufferAllocationCount, i32 %numThreads monotonic
-    br label %end
+then:
+  %baseOffset.1 = atomicrmw add ptr @bufferAllocationCount, i32 %numThreads monotonic
+  br label %end
 
-  end:
-    %baseOffset.2 = phi i32 [ undef, %entry ], [ %baseOffset.1, %then ]
-    %baseOffset = call i32 @subgroupBroadcastFirst(i32 %baseOffset.2) [ "convergencectrl"(token %anchor) ]
-    %offset = add i32 %baseOffset, %relativeThreadIdx
-    ret i32 %offset
-  }
+end:
+  %baseOffset.2 = phi i32 [ undef, %entry ], [ %baseOffset.1, %then ]
+  %baseOffset = call i32 @subgroupBroadcastFirst(i32 %baseOffset.2) [ "convergencectrl"(token %anchor) ]
+  %offset = add i32 %baseOffset, %relativeThreadIdx
+  ret i32 %offset
+}
+```
 
 The key here is that the function really doesn't care which set of threads it
 is being called with. It takes whatever set of threads it can get. What the
 implementation of the function cares about is that the initial
-``@subgroupBallot`` -- which is used to retrieve the bitmask of threads that
+`@subgroupBallot` -- which is used to retrieve the bitmask of threads that
 executed the anchor together -- executes with the same set of threads as the
-final ``@subgroupBroadcastFirst``. Nothing else is required for correctness as
+final `@subgroupBroadcastFirst`. Nothing else is required for correctness as
 far as convergence is concerned.
 
-The function ``@reserveSpaceInBuffer`` itself is _not_ ``convergent``: callers
+The function `@reserveSpaceInBuffer` itself is \_not\_ `convergent`: callers
 are free to move call sites of the function as they see fit. This can change
 the behavior in practice, by changing the sets of threads that are grouped
 together for the atomic operation. This can be visible in the output of the
 program, since the order in which outputs appear in the buffer is changed.
-However, this does not break the overall contract that ``@reserveSpaceInBuffer``
+However, this does not break the overall contract that `@reserveSpaceInBuffer`
 has with its caller -- which makes sense: the order of outputs is
 non-deterministic anyway because of the atomic operation that is involved.
 
@@ -417,158 +405,155 @@ that certain transforms which are usually forbidden by the presence of
 convergent operations are in fact allowed, as long as they don't break up the
 region of code that is controlled by the anchor.
 
-.. _convergence_high-level_break:
+(convergence-high-level-break)=
 
-Extended Cycles: Divergent Exit from a Loop
--------------------------------------------
+### Extended Cycles: Divergent Exit from a Loop
 
-High-level languages typically provide a ``break`` statement that transfers
+High-level languages typically provide a `break` statement that transfers
 control out of a loop statement. In most cases, the loop is structured and hence
 there is no ambiguity about convergence inside the loop. But an ambiguity arises
-when a ``break`` is control dependent on a divergent condition inside the loop.
+when a `break` is control dependent on a divergent condition inside the loop.
 Consider the following example:
 
-.. code-block:: c++
-
-  void example() {
-    // A
-    ...
-    for (...) {
-      // B
-      if (condition) { // divergent condition
-        // C
-        convergent_op();
-        break;
-      }
-      // D
-      ...
+```c++
+void example() {
+  // A
+  ...
+  for (...) {
+    // B
+    if (condition) { // divergent condition
+      // C
+      convergent_op();
+      break;
     }
-    // E
+    // D
+    ...
   }
+  // E
+}
+```
 
-In this program, the call to ``convergent_op()`` is lexically "inside" the ``for``
+In this program, the call to `convergent_op()` is lexically "inside" the `for`
 loop. But when translated to LLVM IR, the basic block B is an exiting block
 ending in a divergent branch, and the basic block C is an exit of the loop.
-Thus, the call to ``convergent_op()`` is outside the loop. This causes a mismatch
+Thus, the call to `convergent_op()` is outside the loop. This causes a mismatch
 between the programmer's expectation and the compiled program. The call should
 be executed convergently on every iteration of the loop, by threads that
 together take the branch to exit the loop. But when compiled, all threads that
 take the divergent exit on different iterations first converge at the beginning
-of basic block C and then together execute the call to ``convergent_op()``.
+of basic block C and then together execute the call to `convergent_op()`.
 
-In this case, :ref:`llvm.experimental.convergence.loop
+In this case, {ref}`llvm.experimental.convergence.loop
 <llvm.experimental.convergence.loop>` can be used to express the desired
 semantics. A call to this intrinsic is placed in the loop header, which tracks
 each iteration of the loop. The token produced by this is used as a
-``convergencectrl`` operand to the convergent call. The semantics of the
-``loop`` intrinsic ensures that the convergent call is performed convergently
+`convergencectrl` operand to the convergent call. The semantics of the
+`loop` intrinsic ensures that the convergent call is performed convergently
 only by those threads that convergently exited the loop in a given iteration.
 
-.. code-block:: llvm
+```llvm
+define void @example() convergent {
+  %entry = call token @llvm.experimental.convergence.entry()
+  br label %for
 
-  define void @example() convergent {
-    %entry = call token @llvm.experimental.convergence.entry()
-    br label %for
+for:
+  %inner = call token @llvm.experimental.convergence.loop() ["convergencectrl"(token %entry)]
+  %for.cond = i1 ...
+  br i1 %for.cond, label %B, label %E
 
-  for:
-    %inner = call token @llvm.experimental.convergence.loop() ["convergencectrl"(token %entry)]
-    %for.cond = i1 ...
-    br i1 %for.cond, label %B, label %E
+B:
+  ...
+  %condition = i1 ...
+  br i1 %condition, label %C, label %D
 
-  B:
-    ...
-    %condition = i1 ...
-    br i1 %condition, label %C, label %D
+C:
+  call void @convergent_op() ["convergencectrl"(token %inner)]
+  br label %E
 
-  C:
-    call void @convergent_op() ["convergencectrl"(token %inner)]
-    br label %E
+D:
+  ...
+  br label %for
 
-  D:
-    ...
-    br label %for
-
-  E:
-    ...
-    ret void
-  }
+E:
+  ...
+  ret void
+}
+```
 
 The LLVM IR version of the same program shows a cycle consisting of the basic
-blocks ``%for``, ``%B`` and ``%D``, while ``%C`` is an exit reached by the
-divergent branch at the end of the exiting block ``%B``. But the use of
-convergence control tokens makes it clear that block ``%C`` must be executed
+blocks `%for`, `%B` and `%D`, while `%C` is an exit reached by the
+divergent branch at the end of the exiting block `%B`. But the use of
+convergence control tokens makes it clear that block `%C` must be executed
 convergently only by those threads that convergently take the exit edge from %B
-to ``%C``. In other words, the convergent execution of ``%C`` is governed by the
-call to the :ref:`llvm.experimental.convergence.loop
+to `%C`. In other words, the convergent execution of `%C` is governed by the
+call to the {ref}`llvm.experimental.convergence.loop
 <llvm.experimental.convergence.loop>` intrinsic inside the cycle. The cycle is
 effectively extended to include all uses of this token that lie outside the
 cycle.
 
-.. _dynamic_instances_and_convergence_tokens:
+(dynamic-instances-and-convergence-tokens)=
 
-Dynamic Instances and Convergence Tokens
-========================================
+## Dynamic Instances and Convergence Tokens
 
-Every execution of an LLVM IR instruction occurs in a :ref:`dynamic instance
+Every execution of an LLVM IR instruction occurs in a {ref}`dynamic instance
 <convergence-dynamic-instances>` of the instruction. Dynamic instances are the
 formal objects by which we talk about communicating threads in convergent
 operations. Dynamic instances are defined for *all* operations in an LLVM
 program, whether convergent or not. Convergence control is primarily about the
 dynamic instances of convergent operations since they affect execution of the
 program through inter-thread communication. The dynamic instances for
-non-convergent operations are relevant for determining :ref:`uniformity
+non-convergent operations are relevant for determining {ref}`uniformity
 <convergence-and-uniformity>` of values.
 
 Dynamic instances produced by the execution of the same *convergent operation*
-by different threads may be :ref:`converged <convergence-definition>`. When
+by different threads may be {ref}`converged <convergence-definition>`. When
 executing a convergent operation, the set of threads that execute converged
 dynamic instances is the set of threads that communicate with each other.
 *Convergence tokens* capture this convergence as described below.
 
-*Convergence tokens* are values of ``token`` type, i.e. they cannot be used in
-``phi`` or ``select`` instructions. A convergence token value represents the
+*Convergence tokens* are values of `token` type, i.e. they cannot be used in
+`phi` or `select` instructions. A convergence token value represents the
 dynamic instance of the instruction that produced it.
 
-Convergent operations may have an optional ``convergencectrl`` operand bundle with
+Convergent operations may have an optional `convergencectrl` operand bundle with
 a convergence token operand to define the set of communicating threads relative
 to the operation that defined the token.
 
-   Let ``U`` be a convergent operation other than a call to a convergence
-   control intrinsic, and ``D`` be the convergent operation that defines
-   the token value used as the ``convergencectrl`` operand to ``U``. Two
-   threads execute converged dynamic instances of ``U`` if and only if the
-   token value in both threads was returned by converged dynamic
-   instances of ``D``.
-
-.. note::
-
-   The text defines convergence token values as representing dynamic instances.
-   But if we were to assume that converged dynamic instances produce the same
-   token value, then we could almost think of the token value as representing a
-   set of threads instead -- specifically, the set ``S`` of threads that
-   executed converged dynamic instances of the defining instruction ``D``.
-
-   In this intuitive picture, when a convergence token value ``T`` is used by a
-   ``convergencectrl`` bundle on an instruction ``I``, then the set of threads that
-   communicates in ``I`` is a subset of the set ``S`` represented by the token value.
-   Specifically, it is the subset of threads that ends up executing ``I`` while
-   using the token value.
-
-   This by itself wouldn't quite work as a definition: what if ``I`` is executed
-   multiple times by the same threads? Which execution of ``I`` in thread 1
-   communicates with which execution of ``I`` in thread 2? Leaning on the notion
-   of dynamic instances gives a robust answer to this question as long as ``D``
-   and ``I`` are at the same loop (or cycle) nesting level.
-
-   The case where ``D`` and ``I`` are at different loop nesting levels is
-   forbidden by the :ref:`static rules <convergence_static_rules>` -- handling
-   that case is the purpose of :ref:`llvm.experimental.convergence.loop
-   <llvm.experimental.convergence.loop>`.
-
-.. _convergence_control_intrinsics:
-
-Convergence Control Intrinsics
-==============================
+> Let `U` be a convergent operation other than a call to a convergence
+> control intrinsic, and `D` be the convergent operation that defines
+> the token value used as the `convergencectrl` operand to `U`. Two
+> threads execute converged dynamic instances of `U` if and only if the
+> token value in both threads was returned by converged dynamic
+> instances of `D`.
+
+:::{note}
+The text defines convergence token values as representing dynamic instances.
+But if we were to assume that converged dynamic instances produce the same
+token value, then we could almost think of the token value as representing a
+set of threads instead -- specifically, the set `S` of threads that
+executed converged dynamic instances of the defining instruction `D`.
+
+In this intuitive picture, when a convergence token value `T` is used by a
+`convergencectrl` bundle on an instruction `I`, then the set of threads that
+communicates in `I` is a subset of the set `S` represented by the token value.
+Specifically, it is the subset of threads that ends up executing `I` while
+using the token value.
+
+This by itself wouldn't quite work as a definition: what if `I` is executed
+multiple times by the same threads? Which execution of `I` in thread 1
+communicates with which execution of `I` in thread 2? Leaning on the notion
+of dynamic instances gives a robust answer to this question as long as `D`
+and `I` are at the same loop (or cycle) nesting level.
+
+The case where `D` and `I` are at different loop nesting levels is
+forbidden by the {ref}`static rules <convergence_static_rules>` -- handling
+that case is the purpose of {ref}`llvm.experimental.convergence.loop
+<llvm.experimental.convergence.loop>`.
+:::
+
+(convergence-control-intrinsics)=
+
+## Convergence Control Intrinsics
 
 This section describes target-independent intrinsics that can be used to
 produce convergence tokens.
@@ -576,14 +561,13 @@ produce convergence tokens.
 Behaviour is undefined if a convergence control intrinsic is called
 indirectly.
 
-.. _llvm.experimental.convergence.entry:
-
-``llvm.experimental.convergence.entry``
-----------------------------------------
+(llvm-experimental-convergence-entry)=
 
-.. code-block:: llvm
+### `llvm.experimental.convergence.entry`
 
-  token @llvm.experimental.convergence.entry() convergent readnone
+```llvm
+token @llvm.experimental.convergence.entry() convergent readnone
+```
 
 This intrinsic is used to tie the dynamic instances inside a function to
 those in the caller.
@@ -591,14 +575,15 @@ those in the caller.
 1. If the function is called from outside the scope of LLVM, the convergence of
    dynamic instances of this intrinsic is environment-defined. For example:
 
-   a. In an OpenCL *kernel launch*, the maximal set of threads that
+   1. In an OpenCL *kernel launch*, the maximal set of threads that
       can communicate outside the memory model is a *workgroup*.
       Hence, a suitable choice is to specify that all the threads from
       a single workgroup in OpenCL execute converged dynamic instances
       of this intrinsic.
-   b. In a C/C++ program, threads are launched independently and can
+   2. In a C/C++ program, threads are launched independently and can
       communicate only through the memory model. Hence the dynamic instances of
       this intrinsic in a C/C++ program are never converged.
+
 2. If the function is called from a call-site in LLVM IR, then two
    threads execute converged dynamic instances of this intrinsic if and
    only if both threads entered the function by executing converged
@@ -610,131 +595,127 @@ precede any other convergent operation in the same basic block.
 
 It is an error if this intrinsic appears in a non-convergent function.
 
-It is an error to specify a ``convergencectrl`` operand bundle at a
+It is an error to specify a `convergencectrl` operand bundle at a
 call to this intrinsic.
 
 Function inlining substitutes this intrinsic with the token from the operand
 bundle. For example:
 
-.. code-block:: c++
-
-  // Before inlining:
+```c++
+// Before inlining:
 
-  void callee() convergent {
-    %tok = call token @llvm.experimental.convergence.entry()
-    convergent_operation(...) [ "convergencectrl"(token %tok) ]
-  }
+void callee() convergent {
+  %tok = call token @llvm.experimental.convergence.entry()
+  convergent_operation(...) [ "convergencectrl"(token %tok) ]
+}
 
-  void main() {
-    %outer = call token @llvm.experimental.convergence.anchor()
-    for (...) {
-      %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
-      callee() [ "convergencectrl"(token %inner) ]
-    }
+void main() {
+  %outer = call token @llvm.experimental.convergence.anchor()
+  for (...) {
+    %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
+    callee() [ "convergencectrl"(token %inner) ]
   }
+}
 
-  // After inlining:
+// After inlining:
 
-  void main() {
-    %outer = call token @llvm.experimental.convergence.anchor()
-    for (...) {
-      %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
-      convergent_operation(...) [ "convergencectrl"(token %inner) ]
-    }
+void main() {
+  %outer = call token @llvm.experimental.convergence.anchor()
+  for (...) {
+    %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
+    convergent_operation(...) [ "convergencectrl"(token %inner) ]
   }
+}
+```
 
-.. _llvm.experimental.convergence.loop:
-
-``llvm.experimental.convergence.loop``
---------------------------------------
+(llvm-experimental-convergence-loop)=
 
-.. code-block:: llvm
+### `llvm.experimental.convergence.loop`
 
-  token @llvm.experimental.convergence.loop() [ "convergencectrl"(token) ] convergent readnone
+```llvm
+token @llvm.experimental.convergence.loop() [ "convergencectrl"(token) ] convergent readnone
+```
 
 This intrinsic represents the place where an imaginary counter is incremented
 for determining convergence inside a control flow cycle.
 
-Let ``U`` be a call to this intrinsic and ``D`` be the convergent operation that
-defines the token value used as the ``convergencectrl`` operand to ``U``. Two
-threads execute converged dynamic instances of ``U`` if and only if:
+Let `U` be a call to this intrinsic and `D` be the convergent operation that
+defines the token value used as the `convergencectrl` operand to `U`. Two
+threads execute converged dynamic instances of `U` if and only if:
 
 1. The token value in both threads was returned by converged dynamic
-   instances of ``D``, and,
-2. There is an integer *n* such that both threads execute ``U`` for the *n*'th time
+   instances of `D`, and,
+2. There is an integer *n* such that both threads execute `U` for the *n*'th time
    with that token value.
 
-It is an error to omit the ``convergencectrl`` operand bundle on a
+It is an error to omit the `convergencectrl` operand bundle on a
 call to this intrinsic.
 
 If this intrinsic occurs in a basic block, then it must precede any other
 convergent operation in the same basic block.
 
-.. _convergence_cycle_heart:
+(convergence-cycle-heart)=
 
 **Heart of a Cycle:**
 
-  If a :ref:`cycle <cycle-terminology>` ``C`` contains an occurrence ``H`` of
-  this intrinsic whose token operand is defined outside ``C``, then ``H`` is
-  called the heart of ``C``.
-
-  .. note::
-
-     The static rules for cycles imply that a heart can occur only in the header
-     of a natural loop. This ensures that the heart closely represents the
-     intuitive notion of a loop iteration. If this restriction is relaxed, the
-     resulting semantics provides a new notion of "cycle iteration" even for
-     irreducible cycles. But this allows a natural loop to have a heart in a
-     node other than its header, which has interesting consequences on the
-     meaning of a loop iteration in terms of convergence. For now, we disallow
-     this situation since its practical application is very rare.
-
-.. _llvm.experimental.convergence.anchor:
-
-``llvm.experimental.convergence.anchor``
-----------------------------------------
-
-.. code-block:: llvm
-
-  token @llvm.experimental.convergence.anchor() convergent readnone
+> If a {ref}`cycle <cycle-terminology>` `C` contains an occurrence `H` of
+> this intrinsic whose token operand is defined outside `C`, then `H` is
+> called the heart of `C`.
+>
+> :::{note}
+> The static rules for cycles imply that a heart can occur only in the header
+> of a natural loop. This ensures that the heart closely represents the
+> intuitive notion of a loop iteration. If this restriction is relaxed, the
+> resulting semantics provides a new notion of "cycle iteration" even for
+> irreducible cycles. But this allows a natural loop to have a heart in a
+> node other than its header, which has interesting consequences on the
+> meaning of a loop iteration in terms of convergence. For now, we disallow
+> this situation since its practical application is very rare.
+> :::
+
+(llvm-experimental-convergence-anchor)=
+
+### `llvm.experimental.convergence.anchor`
+
+```llvm
+token @llvm.experimental.convergence.anchor() convergent readnone
+```
 
 This intrinsic produces an initial convergence token that is independent of
 any "outer scope". The set of threads executing converged dynamic instances of
 this intrinsic is implementation-defined.
 
-It is an error to pass a ``convergencectrl`` operand bundle at a
+It is an error to pass a `convergencectrl` operand bundle at a
 call to this intrinsic.
 
-.. note::
-
-   The expectation is that all threads within a group that "happen to be active
-   at the same time" will execute converged dynamic instances, so that programs
-   can detect the maximal set of threads that can communicate efficiently within
-   some local region of the program.
+:::{note}
+The expectation is that all threads within a group that "happen to be active
+at the same time" will execute converged dynamic instances, so that programs
+can detect the maximal set of threads that can communicate efficiently within
+some local region of the program.
+:::
 
-.. _convergence_uncontrolled:
+(convergence-uncontrolled)=
 
-Uncontrolled Convergent Operations
-==================================
+## Uncontrolled Convergent Operations
 
-Convergent operations with an explicit ``convergencectrl`` operand bundle are
+Convergent operations with an explicit `convergencectrl` operand bundle are
 called *controlled convergent operations*. All other convergent operations are
 said to be *uncontrolled*.
 
 An uncontrolled convergent operation is said to have *implicit convergence
-control* determined by the ``convergent`` attribute alone. The semantics of the
-``convergent`` attribute as implemented in LLVM differs from the documented
+control* determined by the `convergent` attribute alone. The semantics of the
+`convergent` attribute as implemented in LLVM differs from the documented
 semantics. The implementation tries to follow common intuition about convergent
 operations, which remains under-specified. As such, it is not possible to fully
 translate implicit convergence control into explicit convergence control tokens,
 and these two modes cannot be mixed in the same function.
 
-  If a function contains a controlled convergent operation, then all convergent
-  operations in that function must either be controlled operations or calls to
-  the convergence control intrinsics.
+> If a function contains a controlled convergent operation, then all convergent
+> operations in that function must either be controlled operations or calls to
+> the convergence control intrinsics.
 
-Inferring Tokens
-----------------
+### Inferring Tokens
 
 (This section is informational)
 
@@ -746,17 +727,17 @@ uncontrolled convergent operations.
 Some uses of uncontrolled convergent operations may need to satisfy the
 following property:
 
-  For an environment-defined group of threads (such as an OpenCL workgroup or
-  subgroup), if one thread in the group executes a convergent operation, then
-  all threads in the group do so convergently with that thread.
+> For an environment-defined group of threads (such as an OpenCL workgroup or
+> subgroup), if one thread in the group executes a convergent operation, then
+> all threads in the group do so convergently with that thread.
 
 In terms of explicit convergence control, this means that the
-``convergencectrl`` operand on each convergent operation ``X`` must ultimately
-originate from a call to the :ref:`llvm.experimental.convergence.entry
+`convergencectrl` operand on each convergent operation `X` must ultimately
+originate from a call to the {ref}`llvm.experimental.convergence.entry
 <llvm.experimental.convergence.entry>` intrinsic. This preserves the possibility
-that the group of threads that converge on reaching ``X`` is the same group that
+that the group of threads that converge on reaching `X` is the same group that
 originally started executing the program in convergence. In comparison, the
-:ref:`llvm.experimental.convergence.anchor
+{ref}`llvm.experimental.convergence.anchor
 <llvm.experimental.convergence.anchor>` intrinsic captures an
 implementation-defined group of threads, which is insufficient to support the
 above property.
@@ -766,70 +747,65 @@ convergence control tokens is the following procedure, which preserves the above
 mentioned property:
 
 1. Convert every irreducible cycle into a reducible cycle.
-2. Insert a call to :ref:`llvm.experimental.convergence.entry
+2. Insert a call to {ref}`llvm.experimental.convergence.entry
    <llvm.experimental.convergence.entry>` at the start of the entry block of the
    function.
-3. Insert a call to :ref:`llvm.experimental.convergence.loop
+3. Insert a call to {ref}`llvm.experimental.convergence.loop
    <llvm.experimental.convergence.loop>` at the start of every loop header. If
-   this loop is an outermost loop, the ``convergencectrl`` operand is the call
-   to :ref:`llvm.experimental.convergence.entry
+   this loop is an outermost loop, the `convergencectrl` operand is the call
+   to {ref}`llvm.experimental.convergence.entry
    <llvm.experimental.convergence.entry>` in the entry block of the function.
-   Otherwise, the ``convergencectrl`` operand is the call to
-   :ref:`llvm.experimental.convergence.loop
+   Otherwise, the `convergencectrl` operand is the call to
+   {ref}`llvm.experimental.convergence.loop
    <llvm.experimental.convergence.loop>` in the parent loop's header.
-4. For each uncontrolled convergent operation ``X``, add a ``convergencectrl``
-   operand bundle using the token defined by a definition ``D`` that is a
-   :ref:`sibling <cycle-sibling>` to this operation. ``D`` always dominates
-   ``X`` --- if ``X`` is not in any cycle, then ``D`` is a call to
-   :ref:`llvm.experimental.convergence.entry
-   <llvm.experimental.convergence.entry>`; otherwise ``D`` is the heart of the
-   parent cycle of ``X``.
+4. For each uncontrolled convergent operation `X`, add a `convergencectrl`
+   operand bundle using the token defined by a definition `D` that is a
+   {ref}`sibling <cycle-sibling>` to this operation. `D` always dominates
+   `X` --- if `X` is not in any cycle, then `D` is a call to
+   {ref}`llvm.experimental.convergence.entry
+   <llvm.experimental.convergence.entry>`; otherwise `D` is the heart of the
+   parent cycle of `X`.
 
-.. _convergence_static_rules:
+(convergence-static-rules)=
 
-Static Rules
-============
+## Static Rules
 
 A *well-formed* program in LLVM IR must satisfy the following static
 rules about cycles and convergence regions.
 
-Closed Paths
-------------
+### Closed Paths
 
-A :ref:`closed path <cycle-closed-path>` in a CFG is a connected sequence of
+A {ref}`closed path <cycle-closed-path>` in a CFG is a connected sequence of
 nodes and edges in the CFG whose start and end points are the same.
 
 1. Every closed path in the CFG that contains a use of a convergence token T other
    than a use by
-   :ref:`llvm.experimental.convergence.loop <llvm.experimental.convergence.loop>`
+   {ref}`llvm.experimental.convergence.loop <llvm.experimental.convergence.loop>`
    must also contain the definition of T.
-
 2. Every closed path in the CFG that contains two different uses of a convergence
    token T must also contain the definition of T.
-
 3. Every closed path in the CFG that contains uses of two different convergence tokens
    T1 and T2 must also contain the definition of at least one of them.
 
 Taken together, these rules imply that for every closed path C, there can be at most
 one convergence token T which is used in C but defined outside of it, and that
-T can be used only once in C, and only by ``llvm.experimental.convergence.loop``.
+T can be used only once in C, and only by `llvm.experimental.convergence.loop`.
 
 4. In every closed path that contains a use U of a token T but not the
    definition of T, U must dominate all nodes in the closed path.
 
-This implies that ``llvm.experimental.convergence.loop`` can appear as a heart
+This implies that `llvm.experimental.convergence.loop` can appear as a heart
 only in the header of a natural loop.
 
-**Sufficient Conditions:** From the :ref:`properties of cycles
+**Sufficient Conditions:** From the {ref}`properties of cycles
 <cycle-closed-path>`, it is sufficient to prove the above properties
 for cycles instead of closed paths. Briefly, any closed path that violates
 one or more of the above static rules is contained in a cycle that also
 violates the same rule(s).
 
-.. _convergence_region:
+(convergence-region)=
 
-Convergence Regions
--------------------
+### Convergence Regions
 
 The *convergence region* of a convergence token T is the minimal region in
 which T is live and used, i.e., the set of program points dominated by the
@@ -838,45 +814,43 @@ definition D of T from which a use of T can be reached.
 The following static rule about convergence regions must be satisfied by
 valid programs:
 
-   If a convergence region R for a token T1 contains a use of a convergence
-   token T2, then R must also contain the definition of T2. (In other words,
-   convergence regions must be reasonably nested.)
-
-.. note::
+> If a convergence region R for a token T1 contains a use of a convergence
+> token T2, then R must also contain the definition of T2. (In other words,
+> convergence regions must be reasonably nested.)
 
-   For brevity, this document uses the term "convergence region of a token
-   definition ``D``" to actually refer to the convergence region of the token
-   ``T`` defined by ``D``.
+:::{note}
+For brevity, this document uses the term "convergence region of a token
+definition `D`" to actually refer to the convergence region of the token
+`T` defined by `D`.
+:::
 
-.. _inferring_noconvergent:
+(inferring-noconvergent)=
 
-Inferring non-convergence
-=========================
+## Inferring non-convergence
 
 When the target or the environment guarantees that threads do not
 communicate using convergent operations or that threads never diverge,
 the dynamic instances in the program are irrelevant and an optimizer
-may remove any occurrence of the ``convergent`` attribute on a
-call-site or a function and any explicit ``convergencectrl`` operand
+may remove any occurrence of the `convergent` attribute on a
+call-site or a function and any explicit `convergencectrl` operand
 bundle at a call-site.
 
-An optimizer may remove the ``convergent`` attribute and any explicit
-``convergencectrl`` operand bundle from a call-site if it can prove
+An optimizer may remove the `convergent` attribute and any explicit
+`convergencectrl` operand bundle from a call-site if it can prove
 that the execution of this call-site always results in a call to a
 non-convergent function.
 
-An optimizer may remove the ``convergent`` attribute on a function if it can
+An optimizer may remove the `convergent` attribute on a function if it can
 prove that the function does not contain a call to
-:ref:`llvm.experimental.convergence.entry
+{ref}`llvm.experimental.convergence.entry
 <llvm.experimental.convergence.entry>`, or any uncontrolled convergent
 operations.
 
-Memory Model Non-Interaction
-============================
+## Memory Model Non-Interaction
 
 The fact that an operation is convergent has no effect on how it is treated for
-memory model purposes. In particular, an operation that is ``convergent`` and
-``readnone`` does not introduce additional ordering constraints as far as the
+memory model purposes. In particular, an operation that is `convergent` and
+`readnone` does not introduce additional ordering constraints as far as the
 memory model is concerned. There is no implied barrier, neither in the memory
 barrier sense nor in the control barrier sense of synchronizing the execution
 of threads.
@@ -884,160 +858,158 @@ of threads.
 Informational note: Threads that execute converged dynamic instances do not
 necessarily do so at the same time.
 
+## Other Interactions
 
-Other Interactions
-==================
-
-A function can be both ``convergent`` and
-``speculatable``, indicating that the function does not have undefined
+A function can be both `convergent` and
+`speculatable`, indicating that the function does not have undefined
 behavior and has no effects besides calculating its result, but is still
 affected by the set of threads executing this function. This typically
 prevents speculation of calls to the function unless the constraint imposed
-by ``convergent`` is further relaxed by some other means.
+by `convergent` is further relaxed by some other means.
 
-Controlled Maximal Convergence
-==============================
+## Controlled Maximal Convergence
 
-The :ref:`converged-with relation <convergence-definition>` over dynamic
+The {ref}`converged-with relation <convergence-definition>` over dynamic
 instances of each controlled convergent operation is completely defined by the
 semantics of convergence tokens. But the implementation-defined convergence at a
-call to :ref:`llvm.experimental.convergence.anchor
+call to {ref}`llvm.experimental.convergence.anchor
 <llvm.experimental.convergence.anchor>` also depends on the cycle hierarchy
 chosen if it occurs inside an irreducible cycle.
 
-When the token defined by a convergent operation ``D`` is used at another
-convergent operation ``U``, the implementation must ensure that the threads that
-converge at ``U`` are all the threads that reached ``U`` after converging at
-``D``. On most implementations, it is reasonable to assume that only these
-threads are converged at every node they reach on any path from ``D`` to ``U``.
-In other words, the converged-with relation at ``D`` produces groups of threads
+When the token defined by a convergent operation `D` is used at another
+convergent operation `U`, the implementation must ensure that the threads that
+converge at `U` are all the threads that reached `U` after converging at
+`D`. On most implementations, it is reasonable to assume that only these
+threads are converged at every node they reach on any path from `D` to `U`.
+In other words, the converged-with relation at `D` produces groups of threads
 that can converge only within each group, while inside the convergence region of
-``D``.
+`D`.
 
-All this affects the :ref:`maximal converged-with relation
-<convergence-maximal>` over dynamic instances and in turn the :ref:`m-converged
+All this affects the {ref}`maximal converged-with relation
+<convergence-maximal>` over dynamic instances and in turn the {ref}`m-converged
 property <uniformity-analysis>` of static instances in the convergence region of
-``D``.
-
-.. _controlled_maximal_converged_with:
-
-  **Controlled Maximal converged-with Relation**
-
-  1. Dynamic instances of a *convergent operation* are related in the controlled
-     maximal converged-with relation according to the semantics of the convergence
-     control tokens.
-  2. Dynamic instances ``X1`` and ``X2`` produced by different threads for the
-     same *non-convergent operation* ``X`` are related in the controlled maximal
-     converged-with relation if and only if:
-
-     1. Both threads executed converged dynamic instances of every token
-        definition ``D`` such that ``X`` is in the convergence region of ``D``,
-        and,
-     2. Either ``X`` is not contained in any cycle, or, for every cycle ``C``
-        with header ``H`` that contains ``X``:
-
-        - every dynamic instance ``H1`` of ``H`` that precedes ``X1`` in the
-          respective thread is convergence-before ``X2``, and,
-        - every dynamic instance ``H2`` of ``H`` that precedes ``X2`` in the
-          respective thread is convergence-before ``X1``,
-        - without assuming that ``X1`` is converged with ``X2``.
-
-.. _controlled_m_converged:
-
-  **Controlled m-converged Static Instances**
-
-  A node ``X`` in a given CFG is reported to be m-converged if and only if:
-
-  1. For any token definition ``D`` such that ``X`` is inside the convergence region
-     of ``D``, ``D`` itself is m-converged, and,
-  2. Every cycle that contains ``X`` satisfies the following necessary
-     conditions:
-
-     a. Every divergent branch inside the cycle satisfies the :ref:`diverged
-        entry criterion<convergence-diverged-entry>`, and,
-     b. There are no :ref:`diverged paths reaching the
-        cycle<convergence-diverged-outside>` from a divergent branch outside it.
-
-Temporal Divergence at Cycle Exit
----------------------------------
+`D`.
+
+(controlled-maximal-converged-with)=
+
+> **Controlled Maximal converged-with Relation**
+>
+> 1. Dynamic instances of a *convergent operation* are related in the controlled
+>    maximal converged-with relation according to the semantics of the convergence
+>    control tokens.
+>
+> 2. Dynamic instances `X1` and `X2` produced by different threads for the
+>    same *non-convergent operation* `X` are related in the controlled maximal
+>    converged-with relation if and only if:
+>
+>    1. Both threads executed converged dynamic instances of every token
+>       definition `D` such that `X` is in the convergence region of `D`,
+>       and,
+>
+>    2. Either `X` is not contained in any cycle, or, for every cycle `C`
+>       with header `H` that contains `X`:
+>
+>       - every dynamic instance `H1` of `H` that precedes `X1` in the
+>         respective thread is convergence-before `X2`, and,
+>       - every dynamic instance `H2` of `H` that precedes `X2` in the
+>         respective thread is convergence-before `X1`,
+>       - without assuming that `X1` is converged with `X2`.
+
+(controlled-m-converged)=
+
+> **Controlled m-converged Static Instances**
+>
+> A node `X` in a given CFG is reported to be m-converged if and only if:
+>
+> 1. For any token definition `D` such that `X` is inside the convergence region
+>    of `D`, `D` itself is m-converged, and,
+>
+> 2. Every cycle that contains `X` satisfies the following necessary
+>    conditions:
+>
+>    1. Every divergent branch inside the cycle satisfies the {ref}`diverged
+>       entry criterion<convergence-diverged-entry>`, and,
+>    2. There are no {ref}`diverged paths reaching the
+>       cycle<convergence-diverged-outside>` from a divergent branch outside it.
+
+### Temporal Divergence at Cycle Exit
 
 When a cycle has a divergent exit, maximal convergence assumes that all threads
 converge at the exit block. But if a controlled convergent operation outside the
-cycle uses a token defined by an operation ``D`` inside the cycle, the
-convergence region of ``D`` now extends outside the cycle. If two threads
-executed converged dynamic instances of ``D`` before exiting the cycle, then
+cycle uses a token defined by an operation `D` inside the cycle, the
+convergence region of `D` now extends outside the cycle. If two threads
+executed converged dynamic instances of `D` before exiting the cycle, then
 they continue to execute converged dynamic instances of nodes in the convergence
-region of ``D`` outside the cycle. Thus, for a value ``V`` defined inside the
-cycle, any use ``U`` of ``V`` within the convergence region of ``T`` uses the
-output of converged dynamic instances of ``V``. If ``V`` is uniform, then its
-use at such a ``U`` is also uniform. In other words, temporal divergence applies
-only to a use of ``V`` that is outside the convergence region of ``D``.
+region of `D` outside the cycle. Thus, for a value `V` defined inside the
+cycle, any use `U` of `V` within the convergence region of `T` uses the
+output of converged dynamic instances of `V`. If `V` is uniform, then its
+use at such a `U` is also uniform. In other words, temporal divergence applies
+only to a use of `V` that is outside the convergence region of `D`.
 
-Rationales for Static rules about cycles
-========================================
+## Rationales for Static rules about cycles
 
 (This section is informative.)
 
-.. note::
-
-   For convenience, we use the operator ``==`` to represent the relation
-   ``converged-with`` and the operator ``!=`` to represent its negation.
+:::{note}
+For convenience, we use the operator `==` to represent the relation
+`converged-with` and the operator `!=` to represent its negation.
+:::
 
 Consider a loop with (incorrect!) convergence control as in the following
 pseudocode:
 
-.. code-block:: llvm
+```llvm
+; WARNING: Example of incorrect convergence control!
 
-  ; WARNING: Example of incorrect convergence control!
-
-  %anchor = call token @llvm.experimental.convergence.anchor()
-  for (;;) {
-    ...
-    call void @convergent.op() [ "convergencectrl"(token %anchor) ]
-    ...
-  }
+%anchor = call token @llvm.experimental.convergence.anchor()
+for (;;) {
+  ...
+  call void @convergent.op() [ "convergencectrl"(token %anchor) ]
+  ...
+}
+```
 
 This code is forbidden by the first static rule about cycles.
 
 A first formal argument why we have to do this is that the dynamic rule for
 deciding whether two threads execute converged dynamic instances of
-``@convergent.op`` leads to a logical contradiction in this code.
+`@convergent.op` leads to a logical contradiction in this code.
 Assume two threads execute converged dynamic instances of the anchor
 followed by two iterations of the loop. Thread 1 executes dynamic instances
-I1 and I2 of ``@convergent.op``, thread 2 executes dynamic instances J1 and J2.
+I1 and I2 of `@convergent.op`, thread 2 executes dynamic instances J1 and J2.
 Using all the rules, we can deduce:
 
-1. ``I1 != I2`` and ``J1 != J2`` by the basic rules of dynamic instances.
+1. `I1 != I2` and `J1 != J2` by the basic rules of dynamic instances.
 
-2. ``I1 == J1`` by the first dynamic rule about controlled convergent
+2. `I1 == J1` by the first dynamic rule about controlled convergent
    operations: both threads execute the same static instruction while using
    a convergence token value produced by converged dynamic instances of an
    instruction (the anchor).
 
-3. ``I1 == J2`` by the same argument. Also, ``I2 == J1`` and ``I2 == J2``.
+3. `I1 == J2` by the same argument. Also, `I2 == J1` and `I2 == J2`.
 
-   The fact that one may be *intuitively* tempted to think of ``I1`` and ``J2``
+   The fact that one may be *intuitively* tempted to think of `I1` and `J2`
    as being executed in different loop iterations is completely irrelevant for
    the *formal* argument. There is no mechanism in LLVM IR semantics for
    forming associations between loop iterations in different threads, *except*
    for the rules defined in this document -- and the rules in this document
    require a loop heart intrinsic for talking about loop iterations.
 
-4. By transitivity, we have ``I1 == I2`` and ``J1 == J2``. That is a
+4. By transitivity, we have `I1 == I2` and `J1 == J2`. That is a
    contradiction.
 
 This problem goes away by inserting a loop heart intrinsic as follows, which
 establishes a relationship between loop iterations across threads.
 
-.. code-block:: llvm
-
-  %anchor = call token @llvm.experimental.convergence.anchor()
-  for (;;) {
-    %loop = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
-    ...
-    call void @convergent.op() [ "convergencectrl"(token %loop) ]
-    ...
-  }
+```llvm
+%anchor = call token @llvm.experimental.convergence.anchor()
+for (;;) {
+  %loop = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
+  ...
+  call void @convergent.op() [ "convergencectrl"(token %loop) ]
+  ...
+}
+```
 
 In the same scenario of two threads executing converged dynamic instances of the
 anchor and then two iterations of the loop, the dynamic rule about loop heart
@@ -1045,45 +1017,45 @@ intrinsics implies that both threads execute the converged dynamic instances of
 the loop heart intrinsic in their respective first iterations and then again in
 their respective second iterations of the loop.
 
-This then implies that they execute converged dynamic instances ``I1 == J1`` of
-the ``@convergent.op`` in their first iterations and then
-``I2 == J2`` in their second iterations. The rule is an "if and only if" rule,
-so it also implies that ``I1 != J2`` and ``I2 != J1``, because those executions
-see token values of ``%loop`` originating from non-converged dynamic
+This then implies that they execute converged dynamic instances `I1 == J1` of
+the `@convergent.op` in their first iterations and then
+`I2 == J2` in their second iterations. The rule is an "if and only if" rule,
+so it also implies that `I1 != J2` and `I2 != J1`, because those executions
+see token values of `%loop` originating from non-converged dynamic
 instances of the loop intrinsic.
 
 One may ask whether we could change the dynamic rule instead of adding the
 static rule about cycles. That is impractical due to deeper difficulties.
 Consider the following loop, again with incorrect convergence control:
 
-.. code-block:: llvm
-
-  ; WARNING: Example of incorrect convergence control!
+```llvm
+; WARNING: Example of incorrect convergence control!
 
-  ; (A)
-  %anchor = call token @llvm.experimental.convergence.anchor()
-  for (;;) {
-    ; (B)
-    if (condition1) {
-      ; (C)
-      call void @convergent.op.1() [ "convergencectrl"(token %anchor) ]
-    }
-    ; (D)
-    if (condition2) {
-      ; (E)
-      call void @convergent.op.2() [ "convergencectrl"(token %anchor) ]
-    }
-    ; (F)
+; (A)
+%anchor = call token @llvm.experimental.convergence.anchor()
+for (;;) {
+  ; (B)
+  if (condition1) {
+    ; (C)
+    call void @convergent.op.1() [ "convergencectrl"(token %anchor) ]
+  }
+  ; (D)
+  if (condition2) {
+    ; (E)
+    call void @convergent.op.2() [ "convergencectrl"(token %anchor) ]
   }
-  ; (G)
+  ; (F)
+}
+; (G)
+```
 
 Assume two threads execute converged dynamic instances of the anchor followed
 by this sequence of basic blocks:
 
-.. code-block:: text
-
-  Thread 1: A B C D F B D E F G
-  Thread 2: A B D E F B C D F G
+```text
+Thread 1: A B C D F B D E F G
+Thread 2: A B D E F B C D F G
+```
 
 That is, both threads execute two iterations of the loop, but they execute
 the different convergent operations in different iterations. Without forming a
@@ -1094,94 +1066,91 @@ same across the threads, if any.
 Again, this can be addressed by adding a loop heart intrinsic, most naturally
 as:
 
-.. code-block:: llvm
-
-  ; (A)
-  %anchor = call token @llvm.experimental.convergence.anchor()
-  for (;;) {
-    ; (B)
-    %loop = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
-    if (condition1) {
-      ; (C)
-      call void @convergent.op.1() [ "convergencectrl"(token %loop) ]
-    }
-    ; (D)
-    if (condition2) {
-      ; (E)
-      call void @convergent.op.2() [ "convergencectrl"(token %loop) ]
-    }
-    ; (F)
+```llvm
+; (A)
+%anchor = call token @llvm.experimental.convergence.anchor()
+for (;;) {
+  ; (B)
+  %loop = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
+  if (condition1) {
+    ; (C)
+    call void @convergent.op.1() [ "convergencectrl"(token %loop) ]
   }
-  ; (G)
-
-Let ``%loop(i;j)`` be the dynamic instance of ``j``-th execution of the loop
-heart intrinsic by thread ``i``, and analogously ``@op.k(i)`` and ``@op.k(i)``
-the dynamic instances of the execution of ``@convergent.op.k`` by thread ``i``.
+  ; (D)
+  if (condition2) {
+    ; (E)
+    call void @convergent.op.2() [ "convergencectrl"(token %loop) ]
+  }
+  ; (F)
+}
+; (G)
+```
+
+Let `%loop(i;j)` be the dynamic instance of `j`-th execution of the loop
+heart intrinsic by thread `i`, and analogously `@op.k(i)` and `@op.k(i)`
+the dynamic instances of the execution of `@convergent.op.k` by thread `i`.
 Then we have:
 
-1. ``%loop(1;j) == %loop(2;j)`` for ``j = 1, 2`` because of the dynamic rule
+1. `%loop(1;j) == %loop(2;j)` for `j = 1, 2` because of the dynamic rule
    about loop heart intrinsics.
-
-2. ``%loop(i;1) != %loop(i;2)`` for ``i = 1, 2`` because of the basic rule that
+2. `%loop(i;1) != %loop(i;2)` for `i = 1, 2` because of the basic rule that
    different executions by the same thread happen in different dynamic
    instances.
-
-3. ``@op.1(1) != @op.1(2)``, since ``@op.1(1)`` uses the token value of ``%loop``
-   referring to ``%loop(1;1)`` and ``@op.1(2)`` uses that
-   referring to ``%loop(2;2) == %loop(1;2)``, which is different from
-   ``%loop(1;1)``.
-
-4. Similarly, ``@op.2(1) != @op.2(2)``.
+3. `@op.1(1) != @op.1(2)`, since `@op.1(1)` uses the token value of `%loop`
+   referring to `%loop(1;1)` and `@op.1(2)` uses that
+   referring to `%loop(2;2) == %loop(1;2)`, which is different from
+   `%loop(1;1)`.
+4. Similarly, `@op.2(1) != @op.2(2)`.
 
 However, loop heart intrinsics could be inserted differently, at the cost
 of also inserting a free-standing anchor:
 
-.. code-block:: llvm
-
-  ; (A)
-  %anchor = call token @llvm.experimental.convergence.anchor()
-  for (;;) {
-    ; (B)
-    if (condition1) {
-      ; (C)
-      %loop = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
-      call void @convergent.op.1() [ "convergencectrl"(token %loop) ]
-    }
-    ; (D)
-    if (condition2) {
-      ; (E)
-      %free = call token @llvm.experimental.convergence.anchor()
-      call void @convergent.op.2() [ "convergencectrl"(token %free) ]
-    }
-    ; (F)
+```llvm
+; (A)
+%anchor = call token @llvm.experimental.convergence.anchor()
+for (;;) {
+  ; (B)
+  if (condition1) {
+    ; (C)
+    %loop = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
+    call void @convergent.op.1() [ "convergencectrl"(token %loop) ]
+  }
+  ; (D)
+  if (condition2) {
+    ; (E)
+    %free = call token @llvm.experimental.convergence.anchor()
+    call void @convergent.op.2() [ "convergencectrl"(token %free) ]
   }
-  ; (G)
+  ; (F)
+}
+; (G)
+```
 
 This leads to the "unnatural counting of loop iterations" that is also mentioned
-elsewhere. Let ``%loop(i)`` be the dynamic instance of the execution of the
-loop heart intrinsic by thread ``i`` (each thread executes it only once), and
-let ``@op.k(i)`` be as before. Then:
+elsewhere. Let `%loop(i)` be the dynamic instance of the execution of the
+loop heart intrinsic by thread `i` (each thread executes it only once), and
+let `@op.k(i)` be as before. Then:
 
-1. ``%loop(1) == %loop(2)`` because of the dynamic rule about loop heart
+1. `%loop(1) == %loop(2)` because of the dynamic rule about loop heart
    intrinsics.
 
-2. ``@op.1(1) == @op.1(2)`` because ``@op.1(i)`` uses the value of ``%loop``
-   referring to ``%loop(i)``, and ``%loop(1) == %loop(2)``.
+2. `@op.1(1) == @op.1(2)` because `@op.1(i)` uses the value of `%loop`
+   referring to `%loop(i)`, and `%loop(1) == %loop(2)`.
 
-3. Whether ``@op.2(1) == @op.2(2)`` is implementation-defined because of the
-   use of the ``%free`` anchor intrinsic.
+3. Whether `@op.2(1) == @op.2(2)` is implementation-defined because of the
+   use of the `%free` anchor intrinsic.
 
    In practice, they almost certainly have to be non-converged dynamic
    instances. Consider that if an implementation strictly follows the order of
    instructions given in the program, the executions of the threads can be
    "aligned" as follows:
 
-   .. code-block:: text
+   ```text
+   Thread 1: A B         C D F B D E F G
+   Thread 2: A B D E F B C D F         G
+   ```
 
-     Thread 1: A B         C D F B D E F G
-     Thread 2: A B D E F B C D F         G
-
-   So then ``@op.2(1)`` physically executes later than ``@op.2(2)`` and there
+   So then `@op.2(1)` physically executes later than `@op.2(2)` and there
    can be no communication between the threads, which means they execute
    non-converged dynamic instances.
 
@@ -1189,40 +1158,38 @@ let ``@op.k(i)`` be as before. Then:
    dependencies that would enforce this execution order. In that case, a highly
    out-of-order implementation could potentially allow communication. That's
    why the rules defined in this document are silent about whether
-   ``@op.2(1) == @op.2(2)`` or not.
+   `@op.2(1) == @op.2(2)` or not.
 
 This type of convergence control seems relatively unlikely to appear in real
 programs. Its possibility is simply a logical consequence of the model.
 
 An equivalent issue arises if the convergent operations are replaced by nested
-loops with loop heart intrinsics that directly refer to ``%anchor``, hence
+loops with loop heart intrinsics that directly refer to `%anchor`, hence
 the variants of the static rules about cycles that apply to them:
 
-.. code-block:: llvm
-
-  ; WARNING: Example of incorrect convergence control!
+```llvm
+; WARNING: Example of incorrect convergence control!
 
-  %anchor = call token @llvm.experimental.convergence.anchor()
-  for (;;) {
-    if (condition1) {
-      for (;;) {
-        %loop1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
-      }
+%anchor = call token @llvm.experimental.convergence.anchor()
+for (;;) {
+  if (condition1) {
+    for (;;) {
+      %loop1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
     }
-    if (condition2) {
-      for (;;) {
-        %loop2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
-      }
+  }
+  if (condition2) {
+    for (;;) {
+      %loop2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %anchor) ]
     }
   }
+}
+```
 
 There is a cycle (closed walk in the CFG) that goes through both loop heart
-intrinsics using ``%anchor`` but not through the definition of ``%anchor``,
+intrinsics using `%anchor` but not through the definition of `%anchor`,
 so this code is invalid.
 
-
-Examples for the Correctness of Program Transforms
-==================================================
+## Examples for the Correctness of Program Transforms
 
 (This section is informative.)
 
@@ -1239,48 +1206,46 @@ For example, unrolling a loop that does not contain convergent operations
 cannot break any of the guarantees required for convergent operations outside
 of the loop.
 
-
-Loop unrolling examples
------------------------
+### Loop unrolling examples
 
 We consider three kinds of loop unrolling here:
 
-* Partial unrolling with no known trip multiple, so a "tail" is required to
+- Partial unrolling with no known trip multiple, so a "tail" is required to
   collect the remaining elements.
-* Partial unrolling by a trip multiple, so no "tail" is required.
-* Full unrolling, which eliminates the loop.
+- Partial unrolling by a trip multiple, so no "tail" is required.
+- Full unrolling, which eliminates the loop.
 
-The first kind is forbidden when ``@llvm.experimental.convergence.loop`` is
+The first kind is forbidden when `@llvm.experimental.convergence.loop` is
 used. We illustrate the reasoning with some examples.
 
 First, an arbitrary loop that contains convergent operations *can* be unrolled
 in all of these ways, even with "tail", if all convergent operations refer back
 to an anchor inside the loop. For example (in pseudo-code):
 
-.. code-block:: llvm
-
-  while (counter > 0) {
-    %tok = call token @llvm.experimental.convergence.anchor()
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-    counter--;
-  }
+```llvm
+while (counter > 0) {
+  %tok = call token @llvm.experimental.convergence.anchor()
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+  counter--;
+}
+```
 
 This can be unrolled to:
 
-.. code-block:: llvm
-
-  while (counter >= 2) {
-    %tok = call token @llvm.experimental.convergence.anchor()
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-    %tok = call token @llvm.experimental.convergence.anchor()
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-    counter -= 2;
-  }
-  while (counter > 0) {
-    %tok = call token @llvm.experimental.convergence.anchor()
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-    counter--;
-  }
+```llvm
+while (counter >= 2) {
+  %tok = call token @llvm.experimental.convergence.anchor()
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+  %tok = call token @llvm.experimental.convergence.anchor()
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+  counter -= 2;
+}
+while (counter > 0) {
+  %tok = call token @llvm.experimental.convergence.anchor()
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+  counter--;
+}
+```
 
 This is likely to change the behavior of the convergent operation if there
 are threads whose initial counter value is not a multiple of 2. In particular,
@@ -1302,33 +1267,33 @@ Unrolling a loop with convergent operations that refer to tokens produced
 outside the loop is forbidden when a "tail" or "remainder" would have to
 be introduced. Consider:
 
-.. code-block:: llvm
-
-  ; (A)
-  %outer = call token @llvm.experimental.convergence.anchor()
-  while (counter > 0) {
-    %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
-    ; (B)
-    call void @convergent.operation() [ "convergencectrl"(token %inner) ]
-    counter--;
-  }
-  ; (C)
+```llvm
+; (A)
+%outer = call token @llvm.experimental.convergence.anchor()
+while (counter > 0) {
+  %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
+  ; (B)
+  call void @convergent.operation() [ "convergencectrl"(token %inner) ]
+  counter--;
+}
+; (C)
+```
 
 To understand why unrolling is forbidden, consider two threads that execute
 converged dynamic instances of the anchor and then proceed with 3 and 4 loop
 iterations, respectively:
 
-.. code-block:: text
-
-  Thread 1: A B B B C
-  Thread 2: A B B B B C
+```text
+Thread 1: A B B B C
+Thread 2: A B B B B C
+```
 
 By the dynamic rule on loop heart intrinsics, these threads execute converged
 dynamic instances of the loop intrinsic for the first 3 iterations, and then
 thread 2 executes another dynamic instance by itself.
 
 By the dynamic rule on general convergent operations, the threads execute
-converged dynamic instances of the ``@convergent.operation`` in the first 3
+converged dynamic instances of the `@convergent.operation` in the first 3
 iterations (that is, the dynamic instance executed by thread 1 in iteration
 *n* is the same as that executed by thread 2 in iteration *n*, for *n = 1,2,3*;
 the dynamic instance executed in iteration 1 is different from that in
@@ -1337,53 +1302,52 @@ iteration 2, etc.).
 Now assume that the loop is unrolled by a factor of 2, which requires a
 remainder as follows:
 
-.. code-block:: llvm
-
-  ; (A)
-  %outer = call token @llvm.experimental.convergence.anchor()
-  while (counter >= 2) {
-    %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
-    ; (B)
-    call void @convergent.operation() [ "convergencectrl"(token %inner) ]
-    call void @convergent.operation() [ "convergencectrl"(token %inner) ]
-    counter -= 2;
-  }
-  ; (C)
-  if (counter > 0) {
-    %remainder = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
-    ; (D)
-    call void @convergent.operation() [ "convergencectrl"(token %remainder) ]
-  }
-  ; (E)
+```llvm
+; (A)
+%outer = call token @llvm.experimental.convergence.anchor()
+while (counter >= 2) {
+  %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
+  ; (B)
+  call void @convergent.operation() [ "convergencectrl"(token %inner) ]
+  call void @convergent.operation() [ "convergencectrl"(token %inner) ]
+  counter -= 2;
+}
+; (C)
+if (counter > 0) {
+  %remainder = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
+  ; (D)
+  call void @convergent.operation() [ "convergencectrl"(token %remainder) ]
+}
+; (E)
+```
 
 First of all, note some interesting problems surrounding the loop intrinsic:
 
 1. It is *not* duplicated inside the unrolled loop. This is to comply with
-   the :ref:`convergence_static_rules`.
-
+   the {ref}`convergence_static_rules`.
 2. It is unclear whether the loop intrinsic ought to be duplicated in the
-   remainder, or whether the final ``@convergent.operation`` in D should just
-   refer to either ``%inner`` (which is possible in SSA form) or directly to
-   ``%outer``. The decision made here is arbitrary and doesn't change the
+   remainder, or whether the final `@convergent.operation` in D should just
+   refer to either `%inner` (which is possible in SSA form) or directly to
+   `%outer`. The decision made here is arbitrary and doesn't change the
    argument that follows. Ultimately, it simply doesn't matter because the
    transform is incorrect either way.
 
 The threads now execute the following sequences of blocks:
 
-.. code-block:: text
-
-  Thread 1: A B C D E
-  Thread 2: A B B C D E
+```text
+Thread 1: A B C D E
+Thread 2: A B B C D E
+```
 
 Analogous to the argument above, they execute converged dynamic instances of the
-``%inner`` intrinsic and the ``@convergent.operation`` in the first iteration
+`%inner` intrinsic and the `@convergent.operation` in the first iteration
 of the unrolled loop, which corresponds to the first 2 iterations of the
 original loop.
 
-However, they execute different static calls to ``@convergent.operation`` for
+However, they execute different static calls to `@convergent.operation` for
 the 3rd iteration of the original loop. In thread 1, that iteration corresponds
 to the call in the remainder, while in thread 2 it corresponds to the first
-call to ``@convergent.operation`` in the unrolled loop. Therefore, they execute
+call to `@convergent.operation` in the unrolled loop. Therefore, they execute
 non-converged dynamic instances, which means that the set of communicating threads
 for the 3rd iteration of the original loop is different. This is why the
 unrolling is incorrect.
@@ -1392,27 +1356,25 @@ On the other hand, unrolling without "tail" is allowed. For example, assuming
 that the trip counter is known to be a multiple of 2, we can unroll the loop
 as follows:
 
-.. code-block:: llvm
-
-  %outer = call token @llvm.experimental.convergence.anchor()
-  while (counter > 0) {
-    %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
-    call void @convergent.operation() [ "convergencectrl"(token %inner) ]
-    call void @convergent.operation() [ "convergencectrl"(token %inner) ]
-    counter -= 2;
-  }
+```llvm
+%outer = call token @llvm.experimental.convergence.anchor()
+while (counter > 0) {
+  %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
+  call void @convergent.operation() [ "convergencectrl"(token %inner) ]
+  call void @convergent.operation() [ "convergencectrl"(token %inner) ]
+  counter -= 2;
+}
+```
 
 Note again that the loop intrinsic is not duplicated.
 
 The
-:ref:`llvm.experimental.convergence.loop <llvm.experimental.convergence.loop>`
+{ref}`llvm.experimental.convergence.loop <llvm.experimental.convergence.loop>`
 intrinsic is typically expected to appear in the header of a natural loop.
 However, it can also appear in non-header blocks of a loop. In that case, the
 loop can generally not be unrolled.
 
-
-Hoisting and sinking
---------------------
+### Hoisting and sinking
 
 In general, hoisting and sinking of convergent operations is forbidden. This is
 because moving the operation to a different point in control flow generally
@@ -1428,127 +1390,127 @@ knowledge.
 For example, hoisting and sinking across *uniform* conditional branches -- i.e.,
 conditional branches where within every possible relevant set of threads, all
 threads will always take the same direction -- is generally allowed. See the end
-of the :ref:`example of reductions inside control flow
+of the {ref}`example of reductions inside control flow
 <convergence_example_reductions>` for a brief discussion.
 
 Some convergent operations can be hoisted but not sunk, or vice versa. A simple
-example is the ``subgroupShuffle(data, id)`` operation. It returns the ``data``
-operand of the thread identified by ``id``, where thread IDs are fixed and
+example is the `subgroupShuffle(data, id)` operation. It returns the `data`
+operand of the thread identified by `id`, where thread IDs are fixed and
 assigned to each thread at launch. The result is undefined (or perhaps there is
-UB, depending on the language and environment) if thread ``id`` is not in the
+UB, depending on the language and environment) if thread `id` is not in the
 communicating set of threads. So hoisting is allowed in the following
 pseudo-code example:
 
-.. code-block:: llvm
-
-  define void @example(...) convergent {
-    %entry = call token @llvm.experimental.convergence.entry()
-    %data = ...
-    %id = ...
-    if (condition) {
-      %shuffled = call i32 @subgroupShuffle(i32 %data, i32 %id) [ "convergencectrl"(token %entry) ]
-      ...
-    } else {
-      %shuffled = call i32 @subgroupShuffle(i32 %data, i32 %id) [ "convergencectrl"(token %entry) ]
-      ...
-    }
+```llvm
+define void @example(...) convergent {
+  %entry = call token @llvm.experimental.convergence.entry()
+  %data = ...
+  %id = ...
+  if (condition) {
+    %shuffled = call i32 @subgroupShuffle(i32 %data, i32 %id) [ "convergencectrl"(token %entry) ]
+    ...
+  } else {
+    %shuffled = call i32 @subgroupShuffle(i32 %data, i32 %id) [ "convergencectrl"(token %entry) ]
+    ...
   }
+}
+```
 
-After hoisting the calls to ``@subgroupShuffle``, the communicating set of
+After hoisting the calls to `@subgroupShuffle`, the communicating set of
 threads is the union of the two sets of threads in the original program, so
-``%id`` can only go "out of range" after hoisting if it did so in the original
+`%id` can only go "out of range" after hoisting if it did so in the original
 program.
 
-However, speculative execution of ``@subgroupShuffle`` in the following program
+However, speculative execution of `@subgroupShuffle` in the following program
 may be forbidden:
 
-.. code-block:: llvm
-
-  define void @example(...) convergent {
-    %entry = call token @llvm.experimental.convergence.entry()
-    %data = ...
-    %id = ...
-    if (condition) {
-      %shuffled = call i32 @subgroupShuffle(i32 %data, i32 %id) [ "convergencectrl"(token %entry) ]
-      ...
-    }
+```llvm
+define void @example(...) convergent {
+  %entry = call token @llvm.experimental.convergence.entry()
+  %data = ...
+  %id = ...
+  if (condition) {
+    %shuffled = call i32 @subgroupShuffle(i32 %data, i32 %id) [ "convergencectrl"(token %entry) ]
+    ...
   }
+}
+```
 
-There is no guarantee about the value of ``%id`` in the threads where
-``condition`` is false. If ``@subgroupShuffle`` is defined to have UB when
-``%id`` is outside of the set of communicating threads, then speculating and
-hoisting ``@subgroupShuffle`` might introduce UB.
+There is no guarantee about the value of `%id` in the threads where
+`condition` is false. If `@subgroupShuffle` is defined to have UB when
+`%id` is outside of the set of communicating threads, then speculating and
+hoisting `@subgroupShuffle` might introduce UB.
 
-On the other hand, if ``@subgroupShuffle`` is defined such that it merely
-produces an undefined value or poison as a result when ``%id`` is "out of range",
+On the other hand, if `@subgroupShuffle` is defined such that it merely
+produces an undefined value or poison as a result when `%id` is "out of range",
 then speculating is okay.
 
 Even though
-:ref:`llvm.experimental.convergence.anchor <llvm.experimental.convergence.anchor>`
-is marked as ``convergent``, it can be sunk in some cases. For example, in
+{ref}`llvm.experimental.convergence.anchor <llvm.experimental.convergence.anchor>`
+is marked as `convergent`, it can be sunk in some cases. For example, in
 pseudo-code:
 
-.. code-block:: llvm
-
-  %tok = call token @llvm.experimental.convergence.anchor()
-  if (condition) {
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-  }
+```llvm
+%tok = call token @llvm.experimental.convergence.anchor()
+if (condition) {
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+}
+```
 
-Assuming that ``%tok`` is only used inside the conditional block, the anchor can
+Assuming that `%tok` is only used inside the conditional block, the anchor can
 be sunk. The rationale is two-fold. First, the anchor has implementation-defined
 behavior, and the sinking is part of the implementation. Second, already in the
 original program, the set of threads that communicates in the
-``@convergent.operation`` is automatically a subset of the threads for which
-``condition`` is true.
+`@convergent.operation` is automatically a subset of the threads for which
+`condition` is true.
 
 Anchors can be hoisted in acyclic control flow. For example:
 
-.. code-block:: llvm
-
-  if (condition) {
-    %tok1 = call token @llvm.experimental.convergence.anchor()
-    call void @convergent.operation() [ "convergencectrl"(token %tok1) ]
-  } else {
-    %tok2 = call token @llvm.experimental.convergence.anchor()
-    call void @convergent.operation() [ "convergencectrl"(token %tok2) ]
-  }
+```llvm
+if (condition) {
+  %tok1 = call token @llvm.experimental.convergence.anchor()
+  call void @convergent.operation() [ "convergencectrl"(token %tok1) ]
+} else {
+  %tok2 = call token @llvm.experimental.convergence.anchor()
+  call void @convergent.operation() [ "convergencectrl"(token %tok2) ]
+}
+```
 
 The anchors can be hoisted, resulting in:
 
-.. code-block:: llvm
-
-  %tok = call token @llvm.experimental.convergence.anchor()
-  if (condition) {
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-  } else {
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-  }
+```llvm
+%tok = call token @llvm.experimental.convergence.anchor()
+if (condition) {
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+} else {
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+}
+```
 
 The behavior is unchanged, since each of the static convergent operations only
-ever communicates with threads that have the same ``condition`` value.
+ever communicates with threads that have the same `condition` value.
 By contrast, hoisting the convergent operations themselves is forbidden.
 
 Hoisting and sinking anchors out of and into loops is forbidden. For example:
 
-.. code-block:: llvm
-
-  for (;;) {
-    %tok = call token @llvm.experimental.convergence.anchor()
-    call void @convergent.operation() [ "convergencectrl"(token %tok) ]
-  }
+```llvm
+for (;;) {
+  %tok = call token @llvm.experimental.convergence.anchor()
+  call void @convergent.operation() [ "convergencectrl"(token %tok) ]
+}
+```
 
 Hoisting the anchor would make the program invalid according to the static
 validity rules. Conversely:
 
-.. code-block:: llvm
-
-  %outer = call token @llvm.experimental.convergence.anchor()
-  while (counter > 0) {
-    %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
-    call void @convergent.operation() [ "convergencectrl"(token %inner) ]
-    counter--;
-  }
+```llvm
+%outer = call token @llvm.experimental.convergence.anchor()
+while (counter > 0) {
+  %inner = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %outer) ]
+  call void @convergent.operation() [ "convergencectrl"(token %inner) ]
+  counter--;
+}
+```
 
 The program would stay valid if the anchor was sunk into the loop, but its
 behavior could end up being different. If the anchor is inside the loop, then
@@ -1556,52 +1518,53 @@ each loop iteration has a new dynamic instance of the anchor, and the set of
 threads participating in those dynamic instances of the anchor could be
 different in arbitrary implementation-defined ways. Via the dynamic rules about
 dynamic instances of convergent operations, this then implies that the set of
-threads executing ``@convergent.operation`` could be different in each loop
+threads executing `@convergent.operation` could be different in each loop
 iteration in arbitrary implementation-defined ways.
 
 Convergent operations can be sunk together with their anchor. Again in
 pseudo-code:
 
-.. code-block:: llvm
+```llvm
+%tok = call token @llvm.experimental.convergence.anchor()
+%a = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
+%b = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
+if (condition) {
+  use(%a, %b)
+}
+```
+
+Assuming that `%tok`, `%a`, and `%b` are only used inside the conditional
+block, all can be sunk together:
 
+```llvm
+if (condition) {
   %tok = call token @llvm.experimental.convergence.anchor()
   %a = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
   %b = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
-  if (condition) {
-    use(%a, %b)
-  }
-
-Assuming that ``%tok``, ``%a``, and ``%b`` are only used inside the conditional
-block, all can be sunk together:
-
-.. code-block:: llvm
-
-  if (condition) {
-    %tok = call token @llvm.experimental.convergence.anchor()
-    %a = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
-    %b = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
-    use(%a, %b)
-  }
+  use(%a, %b)
+}
+```
 
 The rationale is that the anchor intrinsic has implementation-defined behavior,
 and the sinking transform is considered to be part of the implementation:
 the sinking will restrict the set of communicating threads to those for which
-``condition`` is true, but that could have happened in the original program
+`condition` is true, but that could have happened in the original program
 anyway for some arbitrary other reason.
 
-However, sinking *only* the convergent operation producing ``%b`` would be
-incorrect. That would allow threads for which ``condition`` is false to
-communicate at ``%a``, but not at ``%b``, which the original program doesn't
+However, sinking *only* the convergent operation producing `%b` would be
+incorrect. That would allow threads for which `condition` is false to
+communicate at `%a`, but not at `%b`, which the original program doesn't
 allow.
 
 Note that the entry intrinsic behaves differently. Sinking the convergent
 operations is forbidden in the following snippet:
 
-.. code-block:: llvm
+```llvm
+%tok = call token @llvm.experimental.convergence.entry()
+%a = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
+%b = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
+if (condition) {
+  use(%a, %b)
+}
+```
 
-  %tok = call token @llvm.experimental.convergence.entry()
-  %a = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
-  %b = call T @pure.convergent.operation(...) [ "convergencectrl"(token %tok) ]
-  if (condition) {
-    use(%a, %b)
-  }
diff --git a/llvm/docs/DependenceGraphs/index.md b/llvm/docs/DependenceGraphs/index.md
index 62f32d52796b9..b47e574969523 100644
--- a/llvm/docs/DependenceGraphs/index.md
+++ b/llvm/docs/DependenceGraphs/index.md
@@ -1,22 +1,19 @@
-=========================
-Dependence Graphs in LLVM
-=========================
+# Dependence Graphs in LLVM
 
+## Introduction
 
-Introduction
-============
 Dependence graphs are useful tools in compilers for analyzing relationships
 between various program elements to help guide optimizations. The ideas
-behind these graphs are described in papers [1]_ and [2]_.
+behind these graphs are described in papers [^footnote-1] and [^footnote-2].
 
 The implementation of these ideas in LLVM may be slightly different than
 what is mentioned in the papers. These differences are documented in
-the `implementation details <implementation-details_>`_.
+the [implementation details][implementation-details].
 
-.. _DataDependenceGraph:
+(datadependencegraph)=
+
+## Data Dependence Graph
 
-Data Dependence Graph
-=====================
 In its simplest form the Data Dependence Graph (or DDG) represents data
 dependencies between individual instructions. Each node in such a graph
 represents a single instruction and is referred to as an "atomic" node.
@@ -24,7 +21,7 @@ It is also possible to combine some atomic nodes that have a simple
 def-use dependency between them into larger nodes that contain multiple-
 instructions.
 
-As described in [1]_ the DDG uses graph abstraction to group nodes
+As described in [^footnote-1] the DDG uses graph abstraction to group nodes
 that are part of a strongly connected component of the graph
 into special nodes called pi-blocks. pi-blocks represent cycles of data
 dependency that prevent reordering transformations. Since any strongly
@@ -33,29 +30,29 @@ that form a cycle, pi-blocks are at most one level deep. In other words,
 no pi-blocks are nested inside another pi-block, resulting in a
 hierarchical representation that is at most one level deep.
 
-
 For example, consider the following:
 
-.. code-block:: c++
-
-  for (int i = 1; i < n; i++) {
-    b[i] = c[i] + b[i-1];
-  }
+```c++
+for (int i = 1; i < n; i++) {
+  b[i] = c[i] + b[i-1];
+}
+```
 
 This code contains a statement that has a loop carried dependence on
 itself creating a cycle in the DDG. The figure below illustrates
 how the cycle of dependency is carried through multiple def-use relations
 and a memory access dependency.
 
-.. image:: cycle.png
+```{image} cycle.png
+```
 
 The DDG corresponding to this example would have a pi-block that contains
 all the nodes participating in the cycle, as shown below:
 
-.. image:: cycle_pi.png
+```{image} cycle_pi.png
+```
 
-Program Dependence Graph
-========================
+## Program Dependence Graph
 
 The Program Dependence Graph (or PDG) has a similar structure as the
 DDG, but it is capable of representing both data dependencies and
@@ -63,18 +60,17 @@ control-flow dependencies between program elements such as
 instructions, groups of instructions, basic blocks or groups of
 basic blocks.
 
-High-Level Design
-=================
+## High-Level Design
 
 The DDG and the PDG are both directed graphs and they extend the
-``DirectedGraph`` class. Each implementation extends its corresponding
+`DirectedGraph` class. Each implementation extends its corresponding
 node and edge types resulting in the inheritance relationship depicted
 in the UML diagram below:
 
-.. image:: uml_nodes_and_edges.png
+```{image} uml_nodes_and_edges.png
+```
 
-Graph Construction
-------------------
+### Graph Construction
 
 The graph build algorithm considers dependencies between elements of
 a given set of instructions or basic blocks. Any dependencies coming
@@ -91,48 +87,48 @@ from its concrete representation.
 The following UML diagram depicts the overall structure of the design
 pattern as it applies to the dependence graph implementation.
 
-.. image:: uml_builder_pattern.png
+```{image} uml_builder_pattern.png
+```
 
 Notice that the common code for building the two types of graphs are
-provided in the ``DependenceGraphBuilder`` class, while the ``DDGBuilder``
-and ``PDGBuilder`` control some aspects of how the graph is constructed
-by the way of overriding virtual methods defined in ``DependenceGraphBuilder``.
+provided in the `DependenceGraphBuilder` class, while the `DDGBuilder`
+and `PDGBuilder` control some aspects of how the graph is constructed
+by the way of overriding virtual methods defined in `DependenceGraphBuilder`.
 
 Note also that the steps and the names used in this diagram are for
 illustrative purposes and may be different from those in the actual
 implementation.
 
-Design Trade-offs
------------------
+### Design Trade-offs
 
-Advantages:
-^^^^^^^^^^^
-  - Builder allows graph construction code to be reused for DDG and PDG.
-  - Builder allows us to create DDG and PDG as separate graphs.
-  - DDG nodes and edges are completely disjoint from PDG nodes and edges allowing them to change easily and independently.
+#### Advantages:
 
-Disadvantages:
-^^^^^^^^^^^^^^
-  - Builder may be perceived as over-engineering at first.
-  - There are some similarities between DDG nodes and edges compared to PDG nodes and edges, but there is little reuse of the class definitions.
+> - Builder allows graph construction code to be reused for DDG and PDG.
+> - Builder allows us to create DDG and PDG as separate graphs.
+> - DDG nodes and edges are completely disjoint from PDG nodes and edges allowing them to change easily and independently.
 
-    - This is tolerable given that the node and edge types are fairly simple and there is little code reuse opportunity anyway.
+#### Disadvantages:
 
+> - Builder may be perceived as over-engineering at first.
+>
+> - There are some similarities between DDG nodes and edges compared to PDG nodes and edges, but there is little reuse of the class definitions.
+>
+>   - This is tolerable given that the node and edge types are fairly simple and there is little code reuse opportunity anyway.
 
-.. _implementation-details:
+(implementation-details)=
 
-Implementation Details
-======================
+## Implementation Details
 
 The current implementation of DDG differs slightly from the dependence
-graph described in [1]_ in the following ways:
+graph described in [^footnote-1] in the following ways:
+
+> 1. The graph nodes in the paper represent three main program components, namely *assignment statements*, *for loop headers* and *while loop headers*. In this implementation, DDG nodes naturally represent LLVM IR instructions. An assignment statement in this implementation typically involves a node representing the `store` instruction along with a number of individual nodes computing the right-hand-side of the assignment that connect to the `store` node via a def-use edge. The loop header instructions are not represented as special nodes in this implementation because they have limited uses and can be easily identified, for example, through `LoopAnalysis`.
+> 2. The paper describes five types of dependency edges between nodes namely *loop dependency*, *flow-*, *anti-*, *output-*, and *input-* dependencies. In this implementation *memory* edges represent the *flow-*, *anti-*, *output-*, and *input-* dependencies. However, *loop dependencies* are not made explicit, because they mainly represent association between a loop structure and the program elements inside the loop and this association is fairly obvious in LLVM IR itself.
+> 3. The paper describes two types of pi-blocks; *recurrences* whose bodies are SCCs and *IN* nodes whose bodies are not part of any SCC. In this implementation, pi-blocks are only created for *recurrences*. *IN* nodes remain as simple DDG nodes in the graph.
+
+### References
 
-  1. The graph nodes in the paper represent three main program components, namely *assignment statements*, *for loop headers* and *while loop headers*. In this implementation, DDG nodes naturally represent LLVM IR instructions. An assignment statement in this implementation typically involves a node representing the ``store`` instruction along with a number of individual nodes computing the right-hand-side of the assignment that connect to the ``store`` node via a def-use edge.  The loop header instructions are not represented as special nodes in this implementation because they have limited uses and can be easily identified, for example, through ``LoopAnalysis``.
-  2. The paper describes five types of dependency edges between nodes namely *loop dependency*, *flow-*, *anti-*, *output-*, and *input-* dependencies. In this implementation *memory* edges represent the *flow-*, *anti-*, *output-*, and *input-* dependencies. However, *loop dependencies* are not made explicit, because they mainly represent association between a loop structure and the program elements inside the loop and this association is fairly obvious in LLVM IR itself.
-  3. The paper describes two types of pi-blocks; *recurrences* whose bodies are SCCs and *IN* nodes whose bodies are not part of any SCC. In this implementation, pi-blocks are only created for *recurrences*. *IN* nodes remain as simple DDG nodes in the graph.
+[^footnote-1]: "D. J. Kuck, R. H. Kuhn, D. A. Padua, B. Leasure, and M. Wolfe (1981). DEPENDENCE GRAPHS AND COMPILER OPTIMIZATIONS."
 
+[^footnote-2]: "J. FERRANTE (IBM), K. J. OTTENSTEIN (Michigan Technological University) and JOE D. WARREN (Rice University), 1987. The Program Dependence Graph and Its Use in Optimization."
 
-References
-----------
-.. [1] "D. J. Kuck, R. H. Kuhn, D. A. Padua, B. Leasure, and M. Wolfe (1981). DEPENDENCE GRAPHS AND COMPILER OPTIMIZATIONS."
-.. [2] "J. FERRANTE (IBM), K. J. OTTENSTEIN (Michigan Technological University) and JOE D. WARREN (Rice University), 1987. The Program Dependence Graph and Its Use in Optimization."
diff --git a/llvm/docs/FaultMaps.md b/llvm/docs/FaultMaps.md
index bffd0d0c01cb8..ba712013e5bdc 100644
--- a/llvm/docs/FaultMaps.md
+++ b/llvm/docs/FaultMaps.md
@@ -1,130 +1,123 @@
-==============================
-FaultMaps and implicit checks
-==============================
+# FaultMaps and implicit checks
 
-
-Motivation
-==========
+## Motivation
 
 Code generated by managed language runtimes tends to have checks that
-are required for safety but never fail in practice.  In such cases, it
+are required for safety but never fail in practice. In such cases, it
 is profitable to make the non-failing case cheaper even if it makes
-the failing case significantly more expensive.  This asymmetry can be
+the failing case significantly more expensive. This asymmetry can be
 exploited by folding such safety checks into operations that can be
 made to fault reliably if the check would have failed, and recovering
 from such a fault by using a signal handler.
 
 For example, Java requires null checks on objects before they are read
-from or written to.  If the object is ``null`` then a
-``NullPointerException`` has to be thrown, interrupting normal
-execution.  In practice, however, dereferencing a ``null`` pointer is
+from or written to. If the object is `null` then a
+`NullPointerException` has to be thrown, interrupting normal
+execution. In practice, however, dereferencing a `null` pointer is
 extremely rare in well-behaved Java programs, and typically the null
 check can be folded into a nearby memory operation that operates on
 the same memory location.
 
-The Fault Map Section
-=====================
+## The Fault Map Section
 
 Information about implicit checks generated by LLVM is put in a
-special "fault map" section.  On Darwin this section is named
-``__llvm_faultmaps``.
+special "fault map" section. On Darwin this section is named
+`__llvm_faultmaps`.
 
 The format of this section is
 
-.. code-block:: none
-
-  Header {
-    uint8  : Fault Map Version (current version is 1)
-    uint8  : Reserved (expected to be 0)
-    uint16 : Reserved (expected to be 0)
-  }
-  uint32 : NumFunctions
-  FunctionInfo[NumFunctions] {
-    uint64 : FunctionAddress
-    uint32 : NumFaultingPCs
-    uint32 : Reserved (expected to be 0)
-    FunctionFaultInfo[NumFaultingPCs] {
-      uint32  : FaultKind
-      uint32  : FaultingPCOffset
-      uint32  : HandlerPCOffset
-    }
+```none
+Header {
+  uint8  : Fault Map Version (current version is 1)
+  uint8  : Reserved (expected to be 0)
+  uint16 : Reserved (expected to be 0)
+}
+uint32 : NumFunctions
+FunctionInfo[NumFunctions] {
+  uint64 : FunctionAddress
+  uint32 : NumFaultingPCs
+  uint32 : Reserved (expected to be 0)
+  FunctionFaultInfo[NumFaultingPCs] {
+    uint32  : FaultKind
+    uint32  : FaultingPCOffset
+    uint32  : HandlerPCOffset
   }
+}
+```
 
 FailtKind describes the reason of expected fault. Currently three kind
 of faults are supported:
 
-  1. ``FaultMaps::FaultingLoad`` - fault due to load from memory.
-  2. ``FaultMaps::FaultingLoadStore`` - fault due to instruction load and store.
-  3. ``FaultMaps::FaultingStore`` - fault due to store to memory.
-
-The ``ImplicitNullChecks`` pass
-===============================
+> 1. `FaultMaps::FaultingLoad` - fault due to load from memory.
+> 2. `FaultMaps::FaultingLoadStore` - fault due to instruction load and store.
+> 3. `FaultMaps::FaultingStore` - fault due to store to memory.
 
-The ``ImplicitNullChecks`` pass transforms explicit control flow for
-checking if a pointer is ``null``, like:
+## The `ImplicitNullChecks` pass
 
-.. code-block:: llvm
+The `ImplicitNullChecks` pass transforms explicit control flow for
+checking if a pointer is `null`, like:
 
-    %ptr = call i32* @get_ptr()
-    %ptr_is_null = icmp i32* %ptr, null
-    br i1 %ptr_is_null, label %is_null, label %not_null, !make.implicit !0
+```llvm
+  %ptr = call i32* @get_ptr()
+  %ptr_is_null = icmp i32* %ptr, null
+  br i1 %ptr_is_null, label %is_null, label %not_null, !make.implicit !0
 
-  not_null:
-    %t = load i32, i32* %ptr
-    br label %do_something_with_t
+not_null:
+  %t = load i32, i32* %ptr
+  br label %do_something_with_t
 
-  is_null:
-    call void @HFC()
-    unreachable
+is_null:
+  call void @HFC()
+  unreachable
 
-  !0 = !{}
+!0 = !{}
+```
 
 to control flow implicit in the instruction loading or storing through
 the pointer being null checked:
 
-.. code-block:: llvm
+```llvm
+  %ptr = call i32* @get_ptr()
+  %t = load i32, i32* %ptr  ;; handler-pc = label %is_null
+  br label %do_something_with_t
 
-    %ptr = call i32* @get_ptr()
-    %t = load i32, i32* %ptr  ;; handler-pc = label %is_null
-    br label %do_something_with_t
+is_null:
+  call void @HFC()
+  unreachable
+```
 
-  is_null:
-    call void @HFC()
-    unreachable
+This transform happens at the `MachineInstr` level, not the LLVM IR
+level (so the above example is only representative, not literal). The
+`ImplicitNullChecks` pass runs during codegen, if
+`-enable-implicit-null-checks` is passed to `llc`.
 
-This transform happens at the ``MachineInstr`` level, not the LLVM IR
-level (so the above example is only representative, not literal).  The
-``ImplicitNullChecks`` pass runs during codegen, if
-``-enable-implicit-null-checks`` is passed to ``llc``.
+The `ImplicitNullChecks` pass adds entries to the
+`__llvm_faultmaps` section described above as needed.
 
-The ``ImplicitNullChecks`` pass adds entries to the
-``__llvm_faultmaps`` section described above as needed.
-
-``make.implicit`` metadata
---------------------------
+### `make.implicit` metadata
 
 Making null checks implicit is an aggressive optimization, and it can
 be a net performance pessimization if too many memory operations end
-up faulting because of it.  A language runtime typically needs to
+up faulting because of it. A language runtime typically needs to
 ensure that only a negligible number of implicit null checks actually
-fault once the application has reached a steady state.  A standard way
+fault once the application has reached a steady state. A standard way
 of doing this is by healing failed implicit null checks into explicit
-null checks via code patching or recompilation.  It follows that there
+null checks via code patching or recompilation. It follows that there
 are two requirements an explicit null check needs to satisfy for it to
 be profitable to convert it to an implicit null check:
 
-  1. The case where the pointer is actually null (i.e. the "failing"
-     case) is extremely rare.
-
-  2. The failing path heals the implicit null check into an explicit
-     null check so that the application does not repeatedly page
-     fault.
+> 1. The case where the pointer is actually null (i.e. the "failing"
+>    case) is extremely rare.
+> 2. The failing path heals the implicit null check into an explicit
+>    null check so that the application does not repeatedly page
+>    fault.
 
 The frontend is expected to mark branches that satisfy (1) and (2)
-using a ``!make.implicit`` metadata node (the actual content of the
-metadata node is ignored).  Only branches that are marked with
-``!make.implicit`` metadata are considered as candidates for
+using a `!make.implicit` metadata node (the actual content of the
+metadata node is ignored). Only branches that are marked with
+`!make.implicit` metadata are considered as candidates for
 conversion into implicit null checks.
 
 (Note that while we could deal with (1) using profiling data, dealing
 with (2) requires some information not present in branch profiles.)
+
diff --git a/llvm/docs/FuzzingLLVM.md b/llvm/docs/FuzzingLLVM.md
index f1df0c979c9fe..9cbf863b9ad10 100644
--- a/llvm/docs/FuzzingLLVM.md
+++ b/llvm/docs/FuzzingLLVM.md
@@ -1,177 +1,147 @@
-================================
-Fuzzing LLVM libraries and tools
-================================
+---
+substitutions:
+  LLVM IR fuzzer: '{ref}`structured LLVM IR fuzzer <fuzzing-llvm-ir>`'
+  generic fuzzer: '{ref}`generic fuzzer <fuzzing-llvm-generic>`'
+  protobuf fuzzer: '{ref}`libprotobuf-mutator based fuzzer <fuzzing-llvm-protobuf>`'
+---
 
+# Fuzzing LLVM libraries and tools
 
-Introduction
-============
+## Introduction
 
 The LLVM tree includes a number of fuzzers for various components. These are
-built on top of :doc:`LibFuzzer <LibFuzzer>`. In order to build and run these
-fuzzers, see :ref:`building-fuzzers`.
+built on top of {doc}`LibFuzzer <LibFuzzer>`. In order to build and run these
+fuzzers, see {ref}`building-fuzzers`.
 
+## Available Fuzzers
 
-Available Fuzzers
-=================
+### clang-fuzzer
 
-clang-fuzzer
-------------
+A {{ generic fuzzer }} that tries to compile textual input as C++ code. Some of the
+bugs this fuzzer has reported are [on bugzilla](https://llvm.org/pr23057) and [on OSS Fuzz's
+tracker](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+clang-fuzzer).
 
-A |generic fuzzer| that tries to compile textual input as C++ code. Some of the
-bugs this fuzzer has reported are `on bugzilla`__ and `on OSS Fuzz's
-tracker`__.
+### clang-proto-fuzzer
 
-__ https://llvm.org/pr23057
-__ https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+clang-fuzzer
-
-clang-proto-fuzzer
-------------------
-
-A |protobuf fuzzer| that compiles valid C++ programs generated from a protobuf
+A {{ protobuf fuzzer }} that compiles valid C++ programs generated from a protobuf
 class that describes a subset of the C++ language.
 
 This fuzzer accepts clang command-line options after `ignore_remaining_args=1`.
 For example, the following command will fuzz clang with a higher optimization
 level:
 
-.. code-block:: shell
-
-   % bin/clang-proto-fuzzer <corpus-dir> -ignore_remaining_args=1 -O3
-
-clang-format-fuzzer
--------------------
-
-A |generic fuzzer| that runs clang-format_ on C++ text fragments. Some of the
-bugs this fuzzer has reported are `on bugzilla`__
-and `on OSS Fuzz's tracker`__.
+```shell
+% bin/clang-proto-fuzzer <corpus-dir> -ignore_remaining_args=1 -O3
+```
 
-.. _clang-format: https://clang.llvm.org/docs/ClangFormat.html
-__ https://llvm.org/pr23052
-__ https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+clang-format-fuzzer
+### clang-format-fuzzer
 
-llvm-as-fuzzer
---------------
+A {{ generic fuzzer }} that runs [clang-format][clang-format] on C++ text fragments. Some of the
+bugs this fuzzer has reported are [on bugzilla](https://llvm.org/pr23052)
+and [on OSS Fuzz's tracker](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+clang-format-fuzzer).
 
-A |generic fuzzer| that tries to parse text as :doc:`LLVM assembly <LangRef>`.
-Some of the bugs this fuzzer has reported are `on bugzilla`__.
+### llvm-as-fuzzer
 
-__ https://llvm.org/pr24639
+A {{ generic fuzzer }} that tries to parse text as {doc}`LLVM assembly <LangRef>`.
+Some of the bugs this fuzzer has reported are [on bugzilla](https://llvm.org/pr24639).
 
-llvm-dwarfdump-fuzzer
----------------------
+### llvm-dwarfdump-fuzzer
 
-A |generic fuzzer| that interprets inputs as object files and runs
-:doc:`llvm-dwarfdump <CommandGuide/llvm-dwarfdump>` on them. Some of the bugs
-this fuzzer has reported are `on OSS Fuzz's tracker`__
+A {{ generic fuzzer }} that interprets inputs as object files and runs
+{doc}`llvm-dwarfdump <CommandGuide/llvm-dwarfdump>` on them. Some of the bugs
+this fuzzer has reported are [on OSS Fuzz's tracker](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+llvm-dwarfdump-fuzzer)
 
-__ https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+llvm-dwarfdump-fuzzer
+### llvm-demangle-fuzzer
 
-llvm-demangle-fuzzer
----------------------
-
-A |generic fuzzer| for the Itanium demangler used in various LLVM tools. We've
-fuzzed __cxa_demangle to death, why not fuzz LLVM's implementation of the same
+A {{ generic fuzzer }} for the Itanium demangler used in various LLVM tools. We've
+fuzzed \_\_cxa_demangle to death, why not fuzz LLVM's implementation of the same
 function!
 
-llvm-isel-fuzzer
-----------------
+### llvm-isel-fuzzer
 
-A |LLVM IR fuzzer| aimed at finding bugs in instruction selection.
+A {{ LLVM IR fuzzer }} aimed at finding bugs in instruction selection.
 
 This fuzzer accepts flags after `ignore_remaining_args=1`. The flags match
-those of :doc:`llc <CommandGuide/llc>` and the triple is required. For example,
-the following command would fuzz AArch64 with :doc:`GlobalISel/index`:
-
-.. code-block:: shell
+those of {doc}`llc <CommandGuide/llc>` and the triple is required. For example,
+the following command would fuzz AArch64 with {doc}`GlobalISel/index`:
 
-   % bin/llvm-isel-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple aarch64 -global-isel -O0
+```shell
+% bin/llvm-isel-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple aarch64 -global-isel -O0
+```
 
 Some flags can also be specified in the binary name itself in order to support
 OSS Fuzz, which has trouble with required arguments. To do this, you can copy
-or move ``llvm-isel-fuzzer`` to ``llvm-isel-fuzzer--x-y-z``, separating options
+or move `llvm-isel-fuzzer` to `llvm-isel-fuzzer--x-y-z`, separating options
 from the binary name using "--". The valid options are architecture names
-(``aarch64``, ``x86_64``), optimization levels (``O0``, ``O2``), or specific
-keywords, like ``gisel`` for enabling global instruction selection. In this
+(`aarch64`, `x86_64`), optimization levels (`O0`, `O2`), or specific
+keywords, like `gisel` for enabling global instruction selection. In this
 mode, the same example could be run like so:
 
-.. code-block:: shell
-
-   % bin/llvm-isel-fuzzer--aarch64-O0-gisel <corpus-dir>
+```shell
+% bin/llvm-isel-fuzzer--aarch64-O0-gisel <corpus-dir>
+```
 
-llvm-opt-fuzzer
----------------
+### llvm-opt-fuzzer
 
-A |LLVM IR fuzzer| aimed at finding bugs in optimization passes.
+A {{ LLVM IR fuzzer }} aimed at finding bugs in optimization passes.
 
 It receives an optimization pipeline and runs it for each fuzzer input.
 
-Interface of this fuzzer almost directly mirrors ``llvm-isel-fuzzer``. Both
-``mtriple`` and ``passes`` arguments are required. Passes are specified in a
+Interface of this fuzzer almost directly mirrors `llvm-isel-fuzzer`. Both
+`mtriple` and `passes` arguments are required. Passes are specified in a
 format suitable for the new pass manager. You can find some documentation about
-this format in the doxygen for ``PassBuilder::parsePassPipeline``.
+this format in the doxygen for `PassBuilder::parsePassPipeline`.
 
-.. code-block:: shell
+```shell
+% bin/llvm-opt-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple x86_64 -passes instcombine
+```
 
-   % bin/llvm-opt-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple x86_64 -passes instcombine
-
-Similarly to the ``llvm-isel-fuzzer``, arguments in some predefined configurations
+Similarly to the `llvm-isel-fuzzer`, arguments in some predefined configurations
 might be embedded directly into the binary file name:
 
-.. code-block:: shell
-
-   % bin/llvm-opt-fuzzer--x86_64-instcombine <corpus-dir>
+```shell
+% bin/llvm-opt-fuzzer--x86_64-instcombine <corpus-dir>
+```
 
-llvm-mc-assemble-fuzzer
------------------------
+### llvm-mc-assemble-fuzzer
 
-A |generic fuzzer| that fuzzes the MC layer's assemblers by treating inputs as
+A {{ generic fuzzer }} that fuzzes the MC layer's assemblers by treating inputs as
 target-specific assembly.
 
 Note that this fuzzer has an unusual command line interface which is not fully
 compatible with all of libFuzzer's features. Fuzzer arguments must be passed
-after ``--fuzzer-args``, and any ``llc`` flags must use two dashes. For
+after `--fuzzer-args`, and any `llc` flags must use two dashes. For
 example, to fuzz the AArch64 assembler you might use the following command:
 
-.. code-block:: console
-
-  llvm-mc-fuzzer --triple=aarch64-linux-gnu --fuzzer-args -max_len=4
+```console
+llvm-mc-fuzzer --triple=aarch64-linux-gnu --fuzzer-args -max_len=4
+```
 
 This scheme will likely change in the future.
 
-llvm-mc-disassemble-fuzzer
---------------------------
+### llvm-mc-disassemble-fuzzer
 
-A |generic fuzzer| that fuzzes the MC layer's disassemblers by treating inputs
+A {{ generic fuzzer }} that fuzzes the MC layer's disassemblers by treating inputs
 as assembled binary data.
 
 Note that this fuzzer has an unusual command line interface which is not fully
 compatible with all of libFuzzer's features. See the notes above about
-``llvm-mc-assemble-fuzzer`` for details.
-
-
-.. |generic fuzzer| replace:: :ref:`generic fuzzer <fuzzing-llvm-generic>`
-.. |protobuf fuzzer|
-   replace:: :ref:`libprotobuf-mutator based fuzzer <fuzzing-llvm-protobuf>`
-.. |LLVM IR fuzzer|
-   replace:: :ref:`structured LLVM IR fuzzer <fuzzing-llvm-ir>`
+`llvm-mc-assemble-fuzzer` for details.
 
-lldb-target-fuzzer
----------------------
+### lldb-target-fuzzer
 
-A |generic fuzzer| that interprets inputs as object files and uses them to
+A {{ generic fuzzer }} that interprets inputs as object files and uses them to
 create a target in lldb.
 
-Mutators and Input Generators
-=============================
+## Mutators and Input Generators
 
 The inputs for a fuzz target are generated via random mutations of a
-:ref:`corpus <libfuzzer-corpus>`. There are a few options for the kinds of
+{ref}`corpus <libfuzzer-corpus>`. There are a few options for the kinds of
 mutations that a fuzzer in LLVM might want.
 
-.. _fuzzing-llvm-generic:
+(fuzzing-llvm-generic)=
 
-Generic Random Fuzzing
-----------------------
+### Generic Random Fuzzing
 
 The most basic form of input mutation is to use the built-in mutators of
 LibFuzzer. These simply treat the input corpus as a bag of bits and make random
@@ -179,104 +149,97 @@ mutations. This type of fuzzer is good for stressing the surface layers of a
 program, and is good at testing things like lexers, parsers, or binary
 protocols.
 
-Some of the in-tree fuzzers that use this type of mutator are `clang-fuzzer`_,
-`clang-format-fuzzer`_, `llvm-as-fuzzer`_, `llvm-dwarfdump-fuzzer`_,
-`llvm-mc-assemble-fuzzer`_, and `llvm-mc-disassemble-fuzzer`_.
+Some of the in-tree fuzzers that use this type of mutator are [clang-fuzzer],
+[clang-format-fuzzer], [llvm-as-fuzzer], [llvm-dwarfdump-fuzzer],
+[llvm-mc-assemble-fuzzer], and [llvm-mc-disassemble-fuzzer].
 
-.. _fuzzing-llvm-protobuf:
+(fuzzing-llvm-protobuf)=
 
-Structured Fuzzing using ``libprotobuf-mutator``
-------------------------------------------------
+### Structured Fuzzing using `libprotobuf-mutator`
 
-We can use libprotobuf-mutator_ in order to perform structured fuzzing and
+We can use [libprotobuf-mutator][libprotobuf-mutator] in order to perform structured fuzzing and
 stress deeper layers of programs. This works by defining a protobuf class that
 translates arbitrary data into structurally interesting input. Specifically, we
 use this to work with a subset of the C++ language and perform mutations that
 produce valid C++ programs in order to exercise parts of clang that are more
 interesting than parser error handling.
 
-To build this kind of fuzzer you need `protobuf`_ and its dependencies
+To build this kind of fuzzer you need [protobuf][protobuf] and its dependencies
 installed, and you need to specify some extra flags when configuring the build
-with :doc:`CMake <CMake>`. For example, `clang-proto-fuzzer`_ can be enabled by
-adding ``-DCLANG_ENABLE_PROTO_FUZZER=ON`` to the flags described in
-:ref:`building-fuzzers`.
+with {doc}`CMake <CMake>`. For example, [clang-proto-fuzzer] can be enabled by
+adding `-DCLANG_ENABLE_PROTO_FUZZER=ON` to the flags described in
+{ref}`building-fuzzers`.
 
-The only in-tree fuzzer that uses ``libprotobuf-mutator`` today is
-`clang-proto-fuzzer`_.
+The only in-tree fuzzer that uses `libprotobuf-mutator` today is
+[clang-proto-fuzzer].
 
-.. _libprotobuf-mutator: https://github.com/google/libprotobuf-mutator
-.. _protobuf: https://github.com/google/protobuf
+(fuzzing-llvm-ir)=
 
-.. _fuzzing-llvm-ir:
-
-Structured Fuzzing of LLVM IR
------------------------------
+### Structured Fuzzing of LLVM IR
 
 We also use a more direct form of structured fuzzing for fuzzers that take
-:doc:`LLVM IR <LangRef>` as input. This is achieved through the ``FuzzMutate``
-library, which was `discussed at EuroLLVM 2017`_.
-
-The ``FuzzMutate`` library is used to structurally fuzz backends in
-`llvm-isel-fuzzer`_.
+{doc}`LLVM IR <LangRef>` as input. This is achieved through the `FuzzMutate`
+library, which was [discussed at EuroLLVM 2017][discussed at eurollvm 2017].
 
-.. _discussed at EuroLLVM 2017: https://www.youtube.com/watch?v=UBbQ_s6hNgg
+The `FuzzMutate` library is used to structurally fuzz backends in
+[llvm-isel-fuzzer].
 
+## Building and Running
 
-Building and Running
-====================
+(building-fuzzers)=
 
-.. _building-fuzzers:
-
-Configuring LLVM to Build Fuzzers
----------------------------------
+### Configuring LLVM to Build Fuzzers
 
 Fuzzers will be built and linked to libFuzzer by default as long as you build
 LLVM with sanitizer coverage enabled. You would typically also enable at least
 one sanitizer to find bugs faster. The most common way to build the fuzzers is
 by adding the following two flags to your CMake invocation:
-``-DLLVM_USE_SANITIZER=Address -DLLVM_USE_SANITIZE_COVERAGE=On``.
+`-DLLVM_USE_SANITIZER=Address -DLLVM_USE_SANITIZE_COVERAGE=On`.
 
-.. note:: If you have ``compiler-rt`` checked out in an LLVM tree when building
-          with sanitizers, you'll want to specify ``-DLLVM_BUILD_RUNTIME=Off``
-          to avoid building the sanitizers themselves with sanitizers enabled.
+:::{note}
+If you have `compiler-rt` checked out in an LLVM tree when building
+with sanitizers, you'll want to specify `-DLLVM_BUILD_RUNTIME=Off`
+to avoid building the sanitizers themselves with sanitizers enabled.
+:::
 
-.. note:: You may run into issues if you build with BFD ld, which is the
-          default linker on many Unix systems. These issues are being tracked
-          in https://llvm.org/PR34636.
+:::{note}
+You may run into issues if you build with BFD ld, which is the
+default linker on many Unix systems. These issues are being tracked
+in <https://llvm.org/PR34636>.
+:::
 
-Continuously Running and Finding Bugs
--------------------------------------
+### Continuously Running and Finding Bugs
 
 There used to be a public buildbot running LLVM fuzzers continuously, and while
 this did find issues, it didn't have a very good way to report problems in an
-actionable way. Because of this, we're moving towards using `OSS Fuzz`_ more
+actionable way. Because of this, we're moving towards using [OSS Fuzz][oss fuzz] more
 instead.
 
-You can browse the `LLVM project issue list`_ for the bugs found by
-`LLVM on OSS Fuzz`_. These are also mailed to the `llvm-bugs mailing
-list`_.
-
-.. _OSS Fuzz: https://github.com/google/oss-fuzz
-.. _LLVM project issue list:
-   https://bugs.chromium.org/p/oss-fuzz/issues/list?q=Proj-llvm
-.. _LLVM on OSS Fuzz:
-   https://github.com/google/oss-fuzz/blob/master/projects/llvm
-.. _llvm-bugs mailing list:
-   http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs
-
+You can browse the [LLVM project issue list][llvm project issue list] for the bugs found by
+[LLVM on OSS Fuzz][llvm on oss fuzz]. These are also mailed to the [llvm-bugs mailing
+list][llvm-bugs mailing list].
 
-Utilities for Writing Fuzzers
-=============================
+## Utilities for Writing Fuzzers
 
 There are some utilities available for writing fuzzers in LLVM.
 
 Some helpers for handling the command line interface are available in
-``include/llvm/FuzzMutate/FuzzerCLI.h``, including functions to parse command
+`include/llvm/FuzzMutate/FuzzerCLI.h`, including functions to parse command
 line options in a consistent way and to implement standalone main functions so
 your fuzzer can be built and tested when not built against libFuzzer.
 
 There is also some handling of the CMake config for fuzzers, where you should
-use the ``add_llvm_fuzzer`` to set up fuzzer targets. This function works
-similarly to functions such as ``add_llvm_tool``, but it takes care of linking
-to LibFuzzer when appropriate and can be passed the ``DUMMY_MAIN`` argument to
+use the `add_llvm_fuzzer` to set up fuzzer targets. This function works
+similarly to functions such as `add_llvm_tool`, but it takes care of linking
+to LibFuzzer when appropriate and can be passed the `DUMMY_MAIN` argument to
 enable standalone testing.
+
+[clang-format]: https://clang.llvm.org/docs/ClangFormat.html
+[discussed at eurollvm 2017]: https://www.youtube.com/watch?v=UBbQ_s6hNgg
+[libprotobuf-mutator]: https://github.com/google/libprotobuf-mutator
+[llvm on oss fuzz]: https://github.com/google/oss-fuzz/blob/master/projects/llvm
+[llvm project issue list]: https://bugs.chromium.org/p/oss-fuzz/issues/list?q=Proj-llvm
+[llvm-bugs mailing list]: http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs
+[oss fuzz]: https://github.com/google/oss-fuzz
+[protobuf]: https://github.com/google/protobuf
+
diff --git a/llvm/docs/GetElementPtr.md b/llvm/docs/GetElementPtr.md
index 60ca2c75593ae..9571bd27daa70 100644
--- a/llvm/docs/GetElementPtr.md
+++ b/llvm/docs/GetElementPtr.md
@@ -1,20 +1,15 @@
-=======================================
-The Often Misunderstood GEP Instruction
-=======================================
+# The Often Misunderstood GEP Instruction
 
-
-Introduction
-============
+## Introduction
 
 This document seeks to dispel the mystery and confusion surrounding LLVM's
-`GetElementPtr <LangRef.html#getelementptr-instruction>`_ (GEP) instruction.
+[GetElementPtr](LangRef.html#getelementptr-instruction) (GEP) instruction.
 Questions about the wily GEP instruction are probably the most frequent
 questions once a developer gets down to coding with LLVM. Here we lay
 out the sources of confusion and show that the GEP instruction is really quite
 simple.
 
-Address Computation
-===================
+## Address Computation
 
 When people are first confronted with the GEP instruction, they tend to relate
 it to known concepts from other programming paradigms, most notably C array
@@ -22,8 +17,7 @@ indexing and field selection. GEP closely resembles C array indexing and field
 selection, however it is a little different and this leads to the following
 questions.
 
-What is the first index of the GEP instruction?
------------------------------------------------
+### What is the first index of the GEP instruction?
 
 Quick answer: The index stepping through the second operand.
 
@@ -31,30 +25,30 @@ The confusion with the first index usually arises from thinking about the
 GetElementPtr instruction as if it were a C index operator. They aren't the
 same. For example, when we write, in C:
 
-.. code-block:: c++
-
-  AType *Foo;
-  ...
-  X = &Foo->F;
+```c++
+AType *Foo;
+...
+X = &Foo->F;
+```
 
 it is natural to think that there is only one index, the selection of the field
-``F``.  However, in this example, ``Foo`` is a pointer. That pointer
+`F`. However, in this example, `Foo` is a pointer. That pointer
 must be indexed explicitly in LLVM. C, on the other hand, indexes through it
-transparently.  To arrive at the same address location as the C code, you would
+transparently. To arrive at the same address location as the C code, you would
 provide the GEP instruction with two index operands. The first operand indexes
-through the pointer; the second operand indexes the field ``F`` of the
+through the pointer; the second operand indexes the field `F` of the
 structure, just as if you wrote:
 
-.. code-block:: c++
-
-  X = &Foo[0].F;
+```c++
+X = &Foo[0].F;
+```
 
 Sometimes this question gets rephrased as:
 
-.. _GEP index through first pointer:
+(gep-index-through-first-pointer)=
 
-  *Why is it okay to index through the first pointer, but subsequent pointers
-  won't be dereferenced?*
+> *Why is it okay to index through the first pointer, but subsequent pointers
+> won't be dereferenced?*
 
 The answer is simply because memory does not have to be accessed to perform the
 computation. The second operand to the GEP instruction must be a value of a
@@ -62,41 +56,41 @@ pointer type. The value of the pointer is provided directly to the GEP
 instruction as an operand without any need for accessing memory. It must,
 therefore, be indexed and requires an index operand. Consider this example:
 
-.. code-block:: c++
-
-  struct munger_struct {
-    int f1;
-    int f2;
-  };
-  void munge(struct munger_struct *P) {
-    P[0].f1 = P[1].f1 + P[2].f2;
-  }
-  ...
-  struct munger_struct Array[3];
-  ...
-  munge(Array);
+```c++
+struct munger_struct {
+  int f1;
+  int f2;
+};
+void munge(struct munger_struct *P) {
+  P[0].f1 = P[1].f1 + P[2].f2;
+}
+...
+struct munger_struct Array[3];
+...
+munge(Array);
+```
 
 In this "C" example, the front end compiler (Clang) will generate three GEP
-instructions for the three indices through "P" in the assignment statement.  The
-function argument ``P`` will be the second operand of each of these GEP
-instructions.  The third operand indexes through that pointer.  The fourth
-operand will be the field offset into the ``struct munger_struct`` type, for
-either the ``f1`` or ``f2`` field. So, in LLVM assembly the ``munge`` function
+instructions for the three indices through "P" in the assignment statement. The
+function argument `P` will be the second operand of each of these GEP
+instructions. The third operand indexes through that pointer. The fourth
+operand will be the field offset into the `struct munger_struct` type, for
+either the `f1` or `f2` field. So, in LLVM assembly the `munge` function
 looks like:
 
-.. code-block:: llvm
-
-  define void @munge(ptr %P) {
-  entry:
-    %tmp = getelementptr %struct.munger_struct, ptr %P, i32 1, i32 0
-    %tmp1 = load i32, ptr %tmp
-    %tmp2 = getelementptr %struct.munger_struct, ptr %P, i32 2, i32 1
-    %tmp3 = load i32, ptr %tmp2
-    %tmp4 = add i32 %tmp3, %tmp1
-    %tmp5 = getelementptr %struct.munger_struct, ptr %P, i32 0, i32 0
-    store i32 %tmp4, ptr %tmp5
-    ret void
-  }
+```llvm
+define void @munge(ptr %P) {
+entry:
+  %tmp = getelementptr %struct.munger_struct, ptr %P, i32 1, i32 0
+  %tmp1 = load i32, ptr %tmp
+  %tmp2 = getelementptr %struct.munger_struct, ptr %P, i32 2, i32 1
+  %tmp3 = load i32, ptr %tmp2
+  %tmp4 = add i32 %tmp3, %tmp1
+  %tmp5 = getelementptr %struct.munger_struct, ptr %P, i32 0, i32 0
+  store i32 %tmp4, ptr %tmp5
+  ret void
+}
+```
 
 In each case the second operand is the pointer through which the GEP instruction
 starts. The same is true whether the second operand is an argument, allocated
@@ -104,89 +98,84 @@ memory, or a global variable.
 
 To make this clear, let's consider a more obtuse example:
 
-.. code-block:: text
-
-  @MyVar = external global i32
-  ...
-  %idx1 = getelementptr i32, ptr @MyVar, i64 0
-  %idx2 = getelementptr i32, ptr @MyVar, i64 1
-  %idx3 = getelementptr i32, ptr @MyVar, i64 2
+```text
+ at MyVar = external global i32
+...
+%idx1 = getelementptr i32, ptr @MyVar, i64 0
+%idx2 = getelementptr i32, ptr @MyVar, i64 1
+%idx3 = getelementptr i32, ptr @MyVar, i64 2
+```
 
 These GEP instructions are simply making address computations from the base
-address of ``MyVar``.  They compute, as follows (using C syntax):
-
-.. code-block:: c++
+address of `MyVar`. They compute, as follows (using C syntax):
 
-  idx1 = (char*) &MyVar + 0
-  idx2 = (char*) &MyVar + 4
-  idx3 = (char*) &MyVar + 8
+```c++
+idx1 = (char*) &MyVar + 0
+idx2 = (char*) &MyVar + 4
+idx3 = (char*) &MyVar + 8
+```
 
-Since the type ``i32`` is known to be four bytes long, the indices 0, 1 and 2
+Since the type `i32` is known to be four bytes long, the indices 0, 1 and 2
 translate into memory offsets of 0, 4, and 8, respectively. No memory is
-accessed to make these computations because the address of ``@MyVar`` is passed
+accessed to make these computations because the address of `@MyVar` is passed
 directly to the GEP instructions.
 
-The obtuse part of this example is in the cases of ``%idx2`` and ``%idx3``. They
+The obtuse part of this example is in the cases of `%idx2` and `%idx3`. They
 result in the computation of addresses that point to memory past the end of the
-``@MyVar`` global, which is only one ``i32`` long, not three ``i32``\s long.
+`@MyVar` global, which is only one `i32` long, not three `i32`s long.
 While this is legal in LLVM, it is inadvisable because any load or store with
 the pointer that results from these GEP instructions would trigger undefined
 behavior (UB).
 
-Why is the extra 0 index required?
-----------------------------------
+### Why is the extra 0 index required?
 
 Quick answer: there are no superfluous indices.
 
 This question arises most often when the GEP instruction is applied to a global
 variable which is always a pointer type. For example, consider this:
 
-.. code-block:: text
+```text
+%MyStruct = external global { ptr, i32 }
+...
+%idx = getelementptr { ptr, i32 }, ptr %MyStruct, i64 0, i32 1
+```
 
-  %MyStruct = external global { ptr, i32 }
-  ...
-  %idx = getelementptr { ptr, i32 }, ptr %MyStruct, i64 0, i32 1
-
-The GEP above yields a ``ptr`` by indexing the ``i32`` typed field of the
-structure ``%MyStruct``. When people first look at it, they wonder why the ``i64
-0`` index is needed. However, a closer inspection of how globals and GEPs work
+The GEP above yields a `ptr` by indexing the `i32` typed field of the
+structure `%MyStruct`. When people first look at it, they wonder why the `i64
+0` index is needed. However, a closer inspection of how globals and GEPs work
 reveals the need. Becoming aware of the following facts will dispel the
 confusion:
 
-#. The type of ``%MyStruct`` is *not* ``{ ptr, i32 }`` but rather ``ptr``.
-   That is, ``%MyStruct`` is a pointer (to a structure), not a structure itself.
-
-#. Point #1 is evidenced by noticing the type of the second operand of the GEP
-   instruction (``%MyStruct``) which is ``ptr``.
-
-#. The first index, ``i64 0`` is required to step over the global variable
-   ``%MyStruct``.  Since the second argument to the GEP instruction must always
+1. The type of `%MyStruct` is *not* `{ ptr, i32 }` but rather `ptr`.
+   That is, `%MyStruct` is a pointer (to a structure), not a structure itself.
+2. Point #1 is evidenced by noticing the type of the second operand of the GEP
+   instruction (`%MyStruct`) which is `ptr`.
+3. The first index, `i64 0` is required to step over the global variable
+   `%MyStruct`. Since the second argument to the GEP instruction must always
    be a value of pointer type, the first index steps through that pointer. A
    value of 0 means 0 elements offset from that pointer.
+4. The second index, `i32 1` selects the second field of the structure (the
+   `i32`).
 
-#. The second index, ``i32 1`` selects the second field of the structure (the
-   ``i32``).
-
-What is dereferenced by GEP?
-----------------------------
+### What is dereferenced by GEP?
 
 Quick answer: nothing.
 
 The GetElementPtr instruction dereferences nothing. That is, it doesn't access
-memory in any way. That's what the Load and Store instructions are for.  GEP is
+memory in any way. That's what the Load and Store instructions are for. GEP is
 only involved in the computation of addresses. For example, consider this:
 
-.. code-block:: text
-
-  @MyVar = external global { i32, ptr }
-  ...
-  %idx = getelementptr { i32, ptr }, ptr @MyVar, i64 0, i32 1
-  %arr = load ptr, ptr %idx
-  %idx = getelementptr [40 x i32], ptr %arr, i64 0, i64 17
+```text
+ at MyVar = external global { i32, ptr }
+...
+%idx = getelementptr { i32, ptr }, ptr @MyVar, i64 0, i32 1
+%arr = load ptr, ptr %idx
+%idx = getelementptr [40 x i32], ptr %arr, i64 0, i64 17
+```
 
-In this example, we have a global variable, ``@MyVar``, which is a pointer to
+In this example, we have a global variable, `@MyVar`, which is a pointer to
 a structure containing a pointer. Let's assume that this inner pointer points
-to an array of type ``[40 x i32]``. The above IR will first compute the address
+to an array of type `[40 x i32]`. The above IR will first compute the address
 of the inner pointer, then load the pointer, and then compute the address of
 the 18th array element.
 
@@ -194,18 +183,17 @@ This cannot be expressed in a single GEP instruction, because it requires
 a memory dereference in between. However, the following example would work
 fine:
 
-.. code-block:: text
-
-  @MyVar = external global { i32, [40 x i32 ] }
-  ...
-  %idx = getelementptr { i32, [40 x i32] }, ptr @MyVar, i64 0, i32 1, i64 17
+```text
+ at MyVar = external global { i32, [40 x i32 ] }
+...
+%idx = getelementptr { i32, [40 x i32] }, ptr @MyVar, i64 0, i32 1, i64 17
+```
 
 In this case, the structure does not contain a pointer and the GEP instruction
 can index through the global variable, into the second field of the structure
-and access the 18th ``i32`` in the array there.
+and access the 18th `i32` in the array there.
 
-Why don't GEP x,0,0,1 and GEP x,1 alias?
-----------------------------------------
+### Why don't GEP x,0,0,1 and GEP x,1 alias?
 
 Quick Answer: They compute different address locations.
 
@@ -213,50 +201,46 @@ If you look at the first indices in these GEP instructions you find that they
 are different (0 and 1), therefore the address computation diverges with that
 index. Consider this example:
 
-.. code-block:: llvm
+```llvm
+ at MyVar = external global { [10 x i32] }
+%idx1 = getelementptr { [10 x i32] }, ptr @MyVar, i64 0, i32 0, i64 1
+%idx2 = getelementptr { [10 x i32] }, ptr @MyVar, i64 1
+```
 
-  @MyVar = external global { [10 x i32] }
-  %idx1 = getelementptr { [10 x i32] }, ptr @MyVar, i64 0, i32 0, i64 1
-  %idx2 = getelementptr { [10 x i32] }, ptr @MyVar, i64 1
-
-In this example, ``idx1`` computes the address of the second integer in the
-array that is in the structure in ``@MyVar``, that is ``MyVar+4``.  However,
-``idx2`` computes the address of *the next* structure after ``@MyVar``, that is
-``MyVar+40``, because it indexes past the ten 4-byte integers in ``MyVar``.
+In this example, `idx1` computes the address of the second integer in the
+array that is in the structure in `@MyVar`, that is `MyVar+4`. However,
+`idx2` computes the address of *the next* structure after `@MyVar`, that is
+`MyVar+40`, because it indexes past the ten 4-byte integers in `MyVar`.
 Obviously, in such a situation, the pointers don't alias.
 
-Why do GEP x,1,0,0 and GEP x,1 alias?
--------------------------------------
+### Why do GEP x,1,0,0 and GEP x,1 alias?
 
 Quick Answer: They compute the same address location.
 
 These two GEP instructions will compute the same address because indexing
 through the 0th element does not change the address. Consider this example:
 
-.. code-block:: llvm
-
-  @MyVar = global { [10 x i32] }
-  %idx1 = getelementptr { [10 x i32] }, ptr @MyVar, i64 1, i32 0, i64 0
-  %idx2 = getelementptr { [10 x i32] }, ptr @MyVar, i64 1
+```llvm
+ at MyVar = global { [10 x i32] }
+%idx1 = getelementptr { [10 x i32] }, ptr @MyVar, i64 1, i32 0, i64 0
+%idx2 = getelementptr { [10 x i32] }, ptr @MyVar, i64 1
+```
 
-In this example, the value of ``%idx1`` is ``MyVar+40``, and the value of
-``%idx2`` is also ``MyVar+40``.
+In this example, the value of `%idx1` is `MyVar+40`, and the value of
+`%idx2` is also `MyVar+40`.
 
-Can GEP index into vector elements?
------------------------------------
+### Can GEP index into vector elements?
 
-This hasn't always been forcefully disallowed, though it's not recommended.  It
+This hasn't always been forcefully disallowed, though it's not recommended. It
 leads to awkward special cases in the optimizers, and fundamental inconsistency
 in the IR. In the future, it will probably be outright disallowed.
 
-What effect do address spaces have on GEPs?
--------------------------------------------
+### What effect do address spaces have on GEPs?
 
 None, except that the address space qualifier on the second operand pointer type
 always matches the address space qualifier on the result type.
 
-How is GEP different from ``ptrtoint``, arithmetic, and ``inttoptr``?
----------------------------------------------------------------------
+### How is GEP different from `ptrtoint`, arithmetic, and `inttoptr`?
 
 It's very similar; there are only subtle differences.
 
@@ -271,16 +255,14 @@ Also, GEP carries additional pointer aliasing rules. It's invalid to take a GEP
 from one object, address into a different separately allocated object, and
 dereference it. IR producers (front-ends) must follow this rule, and consumers
 (optimizers, specifically alias analysis) benefit from being able to rely on
-it. See the `Rules`_ section for more information.
+it. See the [Rules] section for more information.
 
 And, GEP is more concise in common cases.
 
 However, for the underlying integer computation implied, there is no
 difference.
 
-
-I'm writing a backend for a target which needs custom lowering for GEP. How do I do this?
------------------------------------------------------------------------------------------
+### I'm writing a backend for a target which needs custom lowering for GEP. How do I do this?
 
 You don't. The integer computation implied by a GEP is target-independent.
 Typically what you'll need to do is make your backend pattern-match expression
@@ -294,15 +276,14 @@ If you require support for addressing units which are not 8 bits, you'll need to
 fix a lot of code in the backend, with GEP lowering being only a small piece of
 the overall picture.
 
-How does VLA addressing work with GEPs?
----------------------------------------
+### How does VLA addressing work with GEPs?
 
 GEPs don't natively support VLAs. LLVM's type system is entirely static, and GEP
 address computations are guided by an LLVM type.
 
 VLA indices can be implemented as linearized indices. For example, an expression
-like ``X[a][b][c]``, must be effectively lowered into a form like
-``X[a*m+b*n+c]``, so that it appears to the GEP as a single-dimensional array
+like `X[a][b][c]`, must be effectively lowered into a form like
+`X[a*m+b*n+c]`, so that it appears to the GEP as a single-dimensional array
 reference.
 
 This means if you want to write an analysis which understands array indices and
@@ -310,13 +291,11 @@ you want to support VLAs, your code will have to be prepared to reverse-engineer
 the linearization. One way to solve this problem is to use the ScalarEvolution
 library, which always presents VLA and non-VLA indexing in the same manner.
 
-.. _Rules:
+(rules)=
 
-Rules
-=====
+## Rules
 
-What happens if an array index is out of bounds?
-------------------------------------------------
+### What happens if an array index is out of bounds?
 
 There are two senses in which an array index can be out of bounds.
 
@@ -333,7 +312,7 @@ valid to compute arbitrary element indices, as the computation only depends on
 the size of the array element, not the number of elements. Note that zero-sized
 arrays are not a special case here.
 
-This sense is unconnected with ``inbounds`` keyword. The ``inbounds`` keyword is
+This sense is unconnected with `inbounds` keyword. The `inbounds` keyword is
 designed to describe low-level pointer arithmetic overflow conditions, rather
 than high-level array indexing rules.
 
@@ -343,30 +322,27 @@ the static array type bounds are respected.
 The second sense of being out of bounds is computing an address that's beyond
 the actual underlying allocated object.
 
-With the ``inbounds`` keyword, the result value of the GEP is ``poison`` if the
+With the `inbounds` keyword, the result value of the GEP is `poison` if the
 address is outside the actual underlying allocated object and not the address
 one-past-the-end.
 
-Without the ``inbounds`` keyword, there are no restrictions on computing
+Without the `inbounds` keyword, there are no restrictions on computing
 out-of-bounds addresses. Obviously, performing a load or a store requires an
 address of allocated and sufficiently aligned memory. But the GEP itself is only
 concerned with computing addresses.
 
-Can array indices be negative?
-------------------------------
+### Can array indices be negative?
 
 Yes. This is basically a special case of array indices being out of bounds.
 
-Can I compare two values computed with GEPs?
---------------------------------------------
+### Can I compare two values computed with GEPs?
 
 Yes. If both addresses are within the same allocated object, or
 one-past-the-end, you'll get the comparison result you expect. If either is
 outside of it, integer arithmetic wrapping may occur, so the comparison may not
 be meaningful.
 
-Can I do GEP with a different pointer type than the type of the underlying object?
-----------------------------------------------------------------------------------
+### Can I do GEP with a different pointer type than the type of the underlying object?
 
 Yes. There are no restrictions on bitcasting a pointer value to an arbitrary
 pointer type. The types in a GEP serve only to define the parameters for the
@@ -378,8 +354,7 @@ the underlying object. Types in this context serve only to specify memory size
 and alignment. Beyond that they are merely a hint to the optimizer indicating
 how the value will likely be used.
 
-Can I cast an object's address to integer and add it to null?
--------------------------------------------------------------
+### Can I cast an object's address to integer and add it to null?
 
 You can compute an address that way, but if you use GEP to do the add, you can't
 use that pointer to actually access the object, unless the object is managed
@@ -389,7 +364,7 @@ The underlying integer computation is sufficiently defined; null has a defined
 value --- zero --- and you can add whatever value you want to it.
 
 However, it's invalid to access (load from or store to) an LLVM-aware object
-with such a pointer. This includes ``GlobalVariables``, ``Allocas``, and objects
+with such a pointer. This includes `GlobalVariables`, `Allocas`, and objects
 pointed to by noalias pointers.
 
 If you really need this functionality, you can do the arithmetic with explicit
@@ -397,8 +372,7 @@ integer instructions, and use inttoptr to convert the result to an address. Most
 of GEP's special aliasing rules do not apply to pointers computed from ptrtoint,
 arithmetic, and inttoptr sequences.
 
-Can I compute the distance between two objects, and add that value to one address to compute the other address?
----------------------------------------------------------------------------------------------------------------
+### Can I compute the distance between two objects, and add that value to one address to compute the other address?
 
 As with arithmetic on null, you can use GEP to compute an address that way, but
 you can't use that pointer to actually access the object if you do, unless the
@@ -407,40 +381,37 @@ object is managed outside of LLVM.
 Also as above, ptrtoint and inttoptr provide an alternative way to do this which
 do not have this restriction.
 
-Can I do type-based alias analysis on LLVM IR?
-----------------------------------------------
+### Can I do type-based alias analysis on LLVM IR?
 
 You can't do type-based alias analysis using LLVM's built-in type system,
 because LLVM has no restrictions on mixing types in addressing, loads or stores.
 
 LLVM's type-based alias analysis pass uses metadata to describe a different type
 system (such as the C type system), and performs type-based aliasing on top of
-that.  Further details are in the
-`language reference <LangRef.html#tbaa-metadata>`_.
+that. Further details are in the
+[language reference](LangRef.html#tbaa-metadata).
 
-What happens if a GEP computation overflows?
---------------------------------------------
+### What happens if a GEP computation overflows?
 
-If the GEP lacks the ``inbounds`` keyword, the value is the result from
+If the GEP lacks the `inbounds` keyword, the value is the result from
 evaluating the implied two's complement integer computation. However, since
 there's no guarantee of where an object will be allocated in the address space,
 such values have limited meaning.
 
-If the GEP has the ``inbounds`` keyword, the result value is ``poison``
+If the GEP has the `inbounds` keyword, the result value is `poison`
 if the GEP overflows (i.e. wraps around the end of the address space).
 
 As such, there are some ramifications of this for inbounds GEPs: scales implied
 by array/vector/pointer indices are always known to be "nsw" since they are
-signed values that are scaled by the element size.  These values are also
-allowed to be negative (e.g. "``gep i32, ptr %P, i32 -1``") but the pointer
-itself is logically treated as an unsigned value.  This means that GEPs have an
+signed values that are scaled by the element size. These values are also
+allowed to be negative (e.g. "`gep i32, ptr %P, i32 -1`") but the pointer
+itself is logically treated as an unsigned value. This means that GEPs have an
 asymmetric relation between the pointer base (which is treated as unsigned) and
 the offset applied to it (which is treated as signed). The result of the
 additions within the offset calculation cannot have signed overflow, but when
 applied to the base pointer, there can be signed overflow.
 
-How can I tell if my front-end is following the rules?
-------------------------------------------------------
+### How can I tell if my front-end is following the rules?
 
 There is currently no checker for the getelementptr rules. Currently, the only
 way to do this is to manually check each place in your front-end where
@@ -452,42 +423,34 @@ the code with dynamic checks though. Alternatively, it would be possible to
 write a static checker which catches a subset of possible problems. However, no
 such checker exists today.
 
-Rationale
-=========
+## Rationale
 
-Why is GEP designed this way?
------------------------------
+### Why is GEP designed this way?
 
 The design of GEP has the following goals, in rough unofficial order of
 priority:
 
-* Support C, C-like languages, and languages which can be conceptually lowered
+- Support C, C-like languages, and languages which can be conceptually lowered
   into C (this covers a lot).
-
-* Support optimizations such as those that are common in C compilers. In
-  particular, GEP is a cornerstone of LLVM's `pointer aliasing
-  model <LangRef.html#pointeraliasing>`_.
-
-* Provide a consistent method for computing addresses so that address
+- Support optimizations such as those that are common in C compilers. In
+  particular, GEP is a cornerstone of LLVM's [pointer aliasing
+  model](LangRef.html#pointeraliasing).
+- Provide a consistent method for computing addresses so that address
   computations don't need to be a part of load and store instructions in the IR.
-
-* Support non-C-like languages, to the extent that it doesn't interfere with
+- Support non-C-like languages, to the extent that it doesn't interfere with
   other goals.
+- Minimize target-specific information in the IR.
 
-* Minimize target-specific information in the IR.
-
-Why do struct member indices always use ``i32``?
-------------------------------------------------
+### Why do struct member indices always use `i32`?
 
 The specific type i32 is probably just a historical artifact, however it's wide
-enough for all practical purposes, so there's been no need to change it.  It
+enough for all practical purposes, so there's been no need to change it. It
 doesn't necessarily imply i32 address arithmetic; it's just an identifier which
 identifies a field in a struct. Requiring that all struct indices be the same
 reduces the range of possibilities for cases where two GEPs are effectively the
 same but have distinct operand types.
 
-What's an uglygep?
-------------------
+### What's an uglygep?
 
 Some LLVM optimizers operate on GEPs by internally lowering them into more
 primitive integer expressions, which allows them to be combined with other
@@ -501,23 +464,18 @@ emit a GEP with the base pointer cast to a simple address-unit pointer, using
 the name "uglygep". This isn't pretty, but it's just as valid, and it's
 sufficient to preserve the pointer aliasing guarantees that GEP provides.
 
-Summary
-=======
+## Summary
 
 In summary, here are some things to always remember about the GetElementPtr
 instruction:
 
-
-#. The GEP instruction never accesses memory, it only provides pointer
+1. The GEP instruction never accesses memory, it only provides pointer
    computations.
-
-#. The second operand to the GEP instruction is always a pointer and it must be
+2. The second operand to the GEP instruction is always a pointer and it must be
    indexed.
-
-#. There are no superfluous indices for the GEP instruction.
-
-#. Trailing zero indices are superfluous for pointer aliasing, but not for the
+3. There are no superfluous indices for the GEP instruction.
+4. Trailing zero indices are superfluous for pointer aliasing, but not for the
    types of the pointers.
-
-#. Leading zero indices are not superfluous for pointer aliasing nor the types
+5. Leading zero indices are not superfluous for pointer aliasing nor the types
    of the pointers.
+
diff --git a/llvm/docs/GitBisecting.md b/llvm/docs/GitBisecting.md
index db13142b1a8af..29f916405f05a 100644
--- a/llvm/docs/GitBisecting.md
+++ b/llvm/docs/GitBisecting.md
@@ -1,13 +1,10 @@
-===================
-Bisecting LLVM code
-===================
+# Bisecting LLVM code
 
-Introduction
-============
+## Introduction
 
-``git bisect`` is a useful tool for finding which revision caused a bug.
+`git bisect` is a useful tool for finding which revision caused a bug.
 
-This document describes how to use ``git bisect``. In particular, while LLVM
+This document describes how to use `git bisect`. In particular, while LLVM
 has a mostly linear history, it has a few merge commits that added projects --
 and these merged the linear history of those projects. As a consequence, the
 LLVM repository has multiple roots: One "normal" root, and then one for each
@@ -15,95 +12,92 @@ toplevel project that was developed out-of-tree and then merged later.
 As of early 2020, the only such merged project is MLIR, but flang will likely
 be merged in a similar way soon.
 
-Basic operation
-===============
+## Basic operation
 
-See https://git-scm.com/docs/git-bisect for a good overview. In summary:
+See <https://git-scm.com/docs/git-bisect> for a good overview. In summary:
 
-  .. code-block:: bash
-
-     git bisect start
-     git bisect bad main
-     git bisect good f00ba
+> ```bash
+> git bisect start
+> git bisect bad main
+> git bisect good f00ba
+> ```
 
 git will check out a revision in between. Try to reproduce your problem at
-that revision, and run ``git bisect good`` or ``git bisect bad``.
+that revision, and run `git bisect good` or `git bisect bad`.
 
 If you can't repro at the current commit (maybe the build is broken), run
-``git bisect skip`` and git will pick a nearby alternate commit.
+`git bisect skip` and git will pick a nearby alternate commit.
 
-(To abort a bisect, run ``git bisect reset``, and if git complains about not
-being able to reset, do the usual ``git checkout -f main; git reset --hard
-origin/main`` dance and try again).
+(To abort a bisect, run `git bisect reset`, and if git complains about not
+being able to reset, do the usual `git checkout -f main; git reset --hard
+origin/main` dance and try again).
 
-``git bisect run``
-==================
+## `git bisect run`
 
 A single bisect step often requires first building clang, and then compiling
 a large code base with just-built clang. This can take a long time, so it's
-good if it can happen completely automatically. ``git bisect run`` can do
+good if it can happen completely automatically. `git bisect run` can do
 this for you if you write a run script that reproduces the problem
 automatically. Writing the script can take 10-20 minutes, but it's almost
 always worth it -- you can do something else while the bisect runs (such
 as writing this document).
 
-Here's an example run script. It assumes that you're in ``llvm-project`` and
-that you have a sibling ``llvm-build-project`` build directory where you
-configured CMake to use Ninja. You have a file ``repro.c`` in the current
+Here's an example run script. It assumes that you're in `llvm-project` and
+that you have a sibling `llvm-build-project` build directory where you
+configured CMake to use Ninja. You have a file `repro.c` in the current
 directory that makes clang crash at trunk, but it worked fine at revision
-``f00ba``.
-
-  .. code-block:: bash
-
-     # Build clang. If the build fails, `exit 125` causes this
-     # revision to be skipped
-     ninja -C ../llvm-build-project clang || exit 125
-
-     ../llvm-build-project/bin/clang repro.c
-
-To make sure your run script works, it's a good idea to run ``./run.sh`` by
-hand and tweak the script until it works, then run ``git bisect good`` or
-``git bisect bad`` manually once based on the result of the script
-(check ``echo $?`` after your script ran), and only then run ``git bisect run
-./run.sh``. Don't forget to mark your run script as executable -- ``git bisect
-run`` doesn't check for that, it just assumes the run script failed each time.
-
-Once your run script works, run ``git bisect run ./run.sh`` and a few hours
+`f00ba`.
+
+> ```bash
+> # Build clang. If the build fails, `exit 125` causes this
+> # revision to be skipped
+> ninja -C ../llvm-build-project clang || exit 125
+>
+> ../llvm-build-project/bin/clang repro.c
+> ```
+
+To make sure your run script works, it's a good idea to run `./run.sh` by
+hand and tweak the script until it works, then run `git bisect good` or
+`git bisect bad` manually once based on the result of the script
+(check `echo $?` after your script ran), and only then run `git bisect run
+./run.sh`. Don't forget to mark your run script as executable -- `git bisect
+run` doesn't check for that, it just assumes the run script failed each time.
+
+Once your run script works, run `git bisect run ./run.sh` and a few hours
 later you'll know which commit caused the regression.
 
 (This is a very simple run script. Often, you want to use just-built clang
 to build a different project and then run a built executable of that project
 in the run script.)
 
-Bisecting across multiple roots
-===============================
+## Bisecting across multiple roots
 
 Here's how LLVM's history currently looks:
 
-  .. code-block:: none
+> ```none
+> A-o-o-......-o-D-o-o-HEAD
+>               /
+>   B-o-...-o-C-
+> ```
 
-     A-o-o-......-o-D-o-o-HEAD
-                   /
-       B-o-...-o-C-
+`A` is the first commit in LLVM ever, `97724f18c79c`.
 
-``A`` is the first commit in LLVM ever, ``97724f18c79c``.
+`B` is the first commit in MLIR, `aed0d21a62db`.
 
-``B`` is the first commit in MLIR, ``aed0d21a62db``.
+`D` is the merge commit that merged MLIR into the main LLVM repository,
+`0f0d0ed1c78f`.
 
-``D`` is the merge commit that merged MLIR into the main LLVM repository,
-``0f0d0ed1c78f``.
+`C` is the last commit in MLIR before it got merged, `0f0d0ed1c78f^2`. (The
+`^n` modifier selects the n'th parent of a merge commit.)
 
-``C`` is the last commit in MLIR before it got merged, ``0f0d0ed1c78f^2``. (The
-``^n`` modifier selects the n'th parent of a merge commit.)
-
-``git bisect`` goes through all parent revisions. Due to the way MLIR was
-merged, at every revision at ``C`` or earlier, *only* the ``mlir/`` directory
+`git bisect` goes through all parent revisions. Due to the way MLIR was
+merged, at every revision at `C` or earlier, *only* the `mlir/` directory
 exists, and nothing else does.
 
-``git bisect --first-parent`` tells git to only descend into the first parent
-of commits, meaning ``B..C`` will never be searched from ``D``.
+`git bisect --first-parent` tells git to only descend into the first parent
+of commits, meaning `B..C` will never be searched from `D`.
+
+## More Resources
 
-More Resources
-==============
+<https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection>
 
-https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection
diff --git a/llvm/docs/GitHubActionsRunners.md b/llvm/docs/GitHubActionsRunners.md
index 3095049b49d3c..d5584a90aef71 100644
--- a/llvm/docs/GitHubActionsRunners.md
+++ b/llvm/docs/GitHubActionsRunners.md
@@ -1,12 +1,10 @@
-===========================
-LLVM GitHub Actions Runners
-===========================
+# LLVM GitHub Actions Runners
 
-.. contents::
-   :local:
+```{contents}
+:local: true
+```
 
-Overview
-========
+## Overview
 
 LLVM's GitHub Actions workflows run on two kinds of runners:
 
@@ -17,33 +15,29 @@ LLVM's GitHub Actions workflows run on two kinds of runners:
   GitHub does not offer, or to provide additional capacity.
 
 Self-hosted runners are organized into sets, and a job selects a set through its
-``runs-on`` labels. Since these machines are shared across the project and are
+`runs-on` labels. Since these machines are shared across the project and are
 available only in limited numbers, workflows that target them should be written
 to use them efficiently and to avoid consuming capacity unnecessarily.
 
 The rest of this document describes the self-hosted runner sets and the
 constraints to keep in mind when writing workflows that target them.
 
-Self-Hosted Linux Runners
-=========================
+## Self-Hosted Linux Runners
 
 This section is a work in progress.
 
-Self-Hosted Windows Runners
-===========================
+## Self-Hosted Windows Runners
 
 This section is a work in progress.
 
-Self-Hosted macOS Runners
-=========================
+## Self-Hosted macOS Runners
 
 Self-hosted runners running macOS arm64 are provided by Apple. These runners can be targeted
-with the following expression ``runs-on: ["self-hosted", "macOS", "apple-runners"]``. Since
+with the following expression `runs-on: ["self-hosted", "macOS", "apple-runners"]`. Since
 these runners have a limited capacity, please contact the infrastructure team before adding
 new jobs that target these runners.
 
-System Version and Architecture
--------------------------------
+### System Version and Architecture
 
 All self-hosted macOS runners run the same version of macOS. However, that version
 is determined by the image used on the runners, which is not controllable from
@@ -56,8 +50,7 @@ All the self-hosted macOS runners run on Apple Silicon, however the exact chip
 version can differ from runner to runner. It is not currently possible to target
 a specific chip version.
 
-Minimize Short-Lived Jobs
--------------------------
+### Minimize Short-Lived Jobs
 
 The macOS runners are relatively expensive to bring up and tear down. Avoid scheduling
 trivial or short-lived work on these runners. For example, do not spin up a macOS runner
@@ -65,41 +58,39 @@ just to perform a cheap check such as determining whether any relevant files hav
 Prefer inexpensive runners instead and only then dispatch a macOS job if testing is actually
 required.
 
-Selecting the Xcode Version
----------------------------
+### Selecting the Xcode Version
 
 The macOS runners come with several versions of Xcode installed: the two latest releases of
-Xcode and the latest beta. You can select the version of Xcode by pointing ``DEVELOPER_DIR``
-to it. The toolchain (``clang``, ``xcrun``, the SDKs, and so on) is then taken from that
-Xcode. This can be done in an early step that writes the variable to ``$GITHUB_ENV`` so that
+Xcode and the latest beta. You can select the version of Xcode by pointing `DEVELOPER_DIR`
+to it. The toolchain (`clang`, `xcrun`, the SDKs, and so on) is then taken from that
+Xcode. This can be done in an early step that writes the variable to `$GITHUB_ENV` so that
 it applies to all subsequent steps:
 
-.. code-block:: yaml
+```yaml
+- name: Select Xcode
+  run: echo "DEVELOPER_DIR=/Applications/Xcode_26.5.app/Contents/Developer" >> $GITHUB_ENV
+```
 
-  - name: Select Xcode
-    run: echo "DEVELOPER_DIR=/Applications/Xcode_26.5.app/Contents/Developer" >> $GITHUB_ENV
+### No Passwordless `sudo`
 
-No Passwordless ``sudo``
-------------------------
-
-The user that runs jobs on the macOS runners cannot use ``sudo``: there is no passwordless
+The user that runs jobs on the macOS runners cannot use `sudo`: there is no passwordless
 sudo, and jobs have no way to supply a password. Any step that requires root privileges will
 therefore fail.
 
-Installing Tools via Homebrew
------------------------------
+### Installing Tools via Homebrew
 
 When a job needs a tool that is not already present on the runner, install it with Homebrew.
-Homebrew installs into a prefix owned by the runner account, so it does not require ``sudo``,
+Homebrew installs into a prefix owned by the runner account, so it does not require `sudo`,
 and it provides self-contained tools. Also make sure you update Homebrew before installing.
 For example:
 
-.. code-block:: yaml
-
-  - name: Install dependencies
-    run: |
-      brew update
-      brew install ninja cmake
+```yaml
+- name: Install dependencies
+  run: |
+    brew update
+    brew install ninja cmake
+```
 
-Version-specific formulae (for example ``python at 3.12``) can be used when a job
+Version-specific formulae (for example `python at 3.12`) can be used when a job
 needs a particular version of a tool.
+
diff --git a/llvm/docs/GwpAsan.md b/llvm/docs/GwpAsan.md
index 65283fa115fb7..33a5dd79f0afb 100644
--- a/llvm/docs/GwpAsan.md
+++ b/llvm/docs/GwpAsan.md
@@ -1,18 +1,14 @@
-========
-GWP-ASan
-========
+# GWP-ASan
 
-
-Introduction
-============
+## Introduction
 
 GWP-ASan is a sampled allocator framework that assists in finding use-after-free
 and heap-buffer-overflow bugs in production environments. It informally is a
-recursive acronym, "**G**\WP-ASan **W**\ill **P**\rovide **A**\llocation
-**SAN**\ity".
+recursive acronym, "**G**WP-ASan **W**ill **P**rovide **A**llocation
+**SAN**ity".
 
 GWP-ASan is based on the classic
-`Electric Fence Malloc Debugger <https://linux.die.net/man/3/efence>`_, with a
+[Electric Fence Malloc Debugger](https://linux.die.net/man/3/efence), with a
 key adaptation. Notably, we only choose a very small percentage of allocations
 to sample, and apply guard pages to these sampled allocations only. The sampling
 is small enough to allow us to have very low performance overhead.
@@ -21,10 +17,9 @@ There is a small, tunable memory overhead that is fixed for the lifetime of the
 process. This is approximately ~40KiB per process using the default settings,
 depending on the average size of your allocations.
 
-GWP-ASan vs. ASan
-=================
+## GWP-ASan vs. ASan
 
-Unlike `AddressSanitizer <https://clang.llvm.org/docs/AddressSanitizer.html>`_,
+Unlike [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html),
 GWP-ASan does not induce a significant performance overhead. ASan often requires
 the use of dedicated canaries to be viable in production environments, and as
 such is often impractical. Moreover, ASan's runtime is not developed with
@@ -39,50 +34,46 @@ the 2x execution slowdown/binary size bloat. For the majority of production
 environments, this impact is too high and security is indispensable, so GWP-ASan
 proves extremely useful.
 
-
-Design
-======
+## Design
 
 **Please note:** The implementation of GWP-ASan is largely in-flux, and these
 details are subject to change. There are currently other implementations of
 GWP-ASan, such as the implementation featured in
-`Chromium <https://cs.chromium.org/chromium/src/components/gwp_asan/>`_. The
+[Chromium](https://cs.chromium.org/chromium/src/components/gwp_asan/). The
 long-term support goal is to ensure feature-parity where reasonable, and to
 support compiler-rt as the reference implementation.
 
-Allocator Support
------------------
+### Allocator Support
 
 GWP-ASan is not a replacement for a traditional allocator. Instead, it works by
 inserting stubs into a supporting allocator to redirect allocations to GWP-ASan
 when they're chosen to be sampled. These stubs are generally implemented in the
-implementation of ``malloc()``, ``free()`` and ``realloc()``. The stubs are
+implementation of `malloc()`, `free()` and `realloc()`. The stubs are
 extremely small, which makes using GWP-ASan in most allocators fairly trivial.
-The stubs follow the same general pattern (example ``malloc()`` pseudocode
+The stubs follow the same general pattern (example `malloc()` pseudocode
 below):
 
-.. code:: cpp
-
-  #ifdef INSTALL_GWP_ASAN_STUBS
-    gwp_asan::GuardedPoolAllocator GWPASanAllocator;
-  #endif
+```cpp
+#ifdef INSTALL_GWP_ASAN_STUBS
+  gwp_asan::GuardedPoolAllocator GWPASanAllocator;
+#endif
 
-  void* YourAllocator::malloc(..) {
-  #ifdef INSTALL_GWP_ASAN_STUBS
-    if (GWPASanAllocator.shouldSample(..))
-      return GWPASanAllocator.allocate(..);
-  #endif
+void* YourAllocator::malloc(..) {
+#ifdef INSTALL_GWP_ASAN_STUBS
+  if (GWPASanAllocator.shouldSample(..))
+    return GWPASanAllocator.allocate(..);
+#endif
 
-    // ... the rest of your allocator code here.
-  }
+  // ... the rest of your allocator code here.
+}
+```
 
 Then, all the supporting allocator needs to do is compile with
-``-DINSTALL_GWP_ASAN_STUBS`` and link against the GWP-ASan library! For
+`-DINSTALL_GWP_ASAN_STUBS` and link against the GWP-ASan library! For
 performance reasons, we strongly recommend static linkage of the GWP-ASan
 library.
 
-Guarded Allocation Pool
------------------------
+### Guarded Allocation Pool
 
 The core of GWP-ASan is the guarded allocation pool. Each sampled allocation is
 backed using its own *guarded* slot, which may consist of one or more accessible
@@ -90,8 +81,7 @@ pages. Each guarded slot is surrounded by two *guard* pages, which are mapped as
 inaccessible. The collection of all guarded slots makes up the *guarded
 allocation pool*.
 
-Buffer Underflow/Overflow Detection
------------------------------------
+### Buffer Underflow/Overflow Detection
 
 We gain buffer-overflow and buffer-underflow detection through these guard
 pages. When a memory access overruns the allocated buffer, it will touch the
@@ -103,8 +93,7 @@ can provide information that will help identify the root cause of the bug.
 Allocations are randomly selected to be either left- or right-aligned to provide
 equal detection of both underflows and overflows.
 
-Use after Free Detection
-------------------------
+### Use after Free Detection
 
 The guarded allocation pool also provides use-after-free detection. Whenever a
 sampled allocation is deallocated, we map its guarded slot as inaccessible. Any
@@ -115,59 +104,56 @@ Please note that the use-after-free detection for a sampled allocation is
 transient. To keep memory overhead fixed while still detecting bugs, deallocated
 slots are randomly reused to guard future allocations.
 
-Usage
-=====
+## Usage
 
 GWP-ASan already ships by default in the
-`Scudo Hardened Allocator <https://llvm.org/docs/ScudoHardenedAllocator.html>`_,
-so building with ``-fsanitize=scudo`` is the quickest and easiest way to try out
+[Scudo Hardened Allocator](https://llvm.org/docs/ScudoHardenedAllocator.html),
+so building with `-fsanitize=scudo` is the quickest and easiest way to try out
 GWP-ASan.
 
-Options
--------
+### Options
 
 GWP-ASan's configuration is managed by the supporting allocator. We provide a
 generic configuration management library that is used by Scudo. It allows
 several aspects of GWP-ASan to be configured through the following methods:
 
 - When the GWP-ASan library is compiled, by setting
-  ``-DGWP_ASAN_DEFAULT_OPTIONS`` to the options string you want set by default.
+  `-DGWP_ASAN_DEFAULT_OPTIONS` to the options string you want set by default.
   If you're building GWP-ASan as part of a compiler-rt/LLVM build, add it during
-  cmake configure time (e.g. ``cmake ... -DGWP_ASAN_DEFAULT_OPTIONS="..."``). If
+  cmake configure time (e.g. `cmake ... -DGWP_ASAN_DEFAULT_OPTIONS="..."`). If
   you're building GWP-ASan outside of compiler-rt, simply ensure that you
-  specify ``-DGWP_ASAN_DEFAULT_OPTIONS="..."`` when building
-  ``optional/options_parser.cpp``).
-
-- By defining a ``__gwp_asan_default_options`` function in one's program that
+  specify `-DGWP_ASAN_DEFAULT_OPTIONS="..."` when building
+  `optional/options_parser.cpp`).
+- By defining a `__gwp_asan_default_options` function in one's program that
   returns the options string to be parsed. Said function must have the following
-  prototype: ``extern "C" const char* __gwp_asan_default_options(void)``, with a
+  prototype: `extern "C" const char* __gwp_asan_default_options(void)`, with a
   default visibility. This will override the compile time define;
-
 - Depending on allocator support (Scudo has support for this mechanism): Through
   an environment variable, containing the options string to be parsed. In Scudo,
   this is through `SCUDO_OPTIONS=GWP_ASAN_${OPTION_NAME}=${VALUE}` (e.g.
   `SCUDO_OPTIONS=GWP_ASAN_SampleRate=100`). Options defined this way will
-  override any definition made through ``__gwp_asan_default_options``.
+  override any definition made through `__gwp_asan_default_options`.
 
 The options string follows a syntax similar to ASan, where distinct options
 can be assigned in the same string, separated by colons.
 
 For example, using the environment variable:
 
-.. code:: console
-
-  GWP_ASAN_OPTIONS="MaxSimultaneousAllocations=16:SampleRate=5000" ./a.out
+```console
+GWP_ASAN_OPTIONS="MaxSimultaneousAllocations=16:SampleRate=5000" ./a.out
+```
 
 Or using the function:
 
-.. code:: cpp
-
-  extern "C" const char *__gwp_asan_default_options() {
-    return "MaxSimultaneousAllocations=16:SampleRate=5000";
-  }
+```cpp
+extern "C" const char *__gwp_asan_default_options() {
+  return "MaxSimultaneousAllocations=16:SampleRate=5000";
+}
+```
 
 The following options are available:
 
+```{eval-rst}
 +----------------------------+---------+--------------------------------------------------------------------------------+
 | Option                     | Default | Description                                                                    |
 +----------------------------+---------+--------------------------------------------------------------------------------+
@@ -193,91 +179,91 @@ The following options are available:
 |                            |         | if the previously installed SIGSEGV handler is SIG_IGN, we terminate the       |
 |                            |         | process after dumping the error report.                                        |
 +----------------------------+---------+--------------------------------------------------------------------------------+
-
-Example
--------
-
-The below code has a use-after-free bug, where the ``string_view`` is created as
-a reference to the temporary result of the ``string+`` operator. The
-use-after-free occurs when ``sv`` is dereferenced on line 8.
-
-.. code:: cpp
-
-  1: #include <iostream>
-  2: #include <string>
-  3: #include <string_view>
-  4:
-  5: int main() {
-  6:   std::string s = "Hellooooooooooooooo ";
-  7:   std::string_view sv = s + "World\n";
-  8:   std::cout << sv;
-  9: }
+```
+
+### Example
+
+The below code has a use-after-free bug, where the `string_view` is created as
+a reference to the temporary result of the `string+` operator. The
+use-after-free occurs when `sv` is dereferenced on line 8.
+
+```cpp
+1: #include <iostream>
+2: #include <string>
+3: #include <string_view>
+4:
+5: int main() {
+6:   std::string s = "Hellooooooooooooooo ";
+7:   std::string_view sv = s + "World\n";
+8:   std::cout << sv;
+9: }
+```
 
 Compiling this code with Scudo+GWP-ASan will probabilistically catch this bug
 and provide us a detailed error report:
 
-.. code:: console
-
-  $ clang++ -fsanitize=scudo -g buggy_code.cpp
-  $ for i in `seq 1 500`; do
-      SCUDO_OPTIONS="GWP_ASAN_SampleRate=100" ./a.out > /dev/null;
-    done
-  |
-  | *** GWP-ASan detected a memory error ***
-  | Use after free at 0x7feccab26000 (0 bytes into a 41-byte allocation at 0x7feccab26000) by thread 31027 here:
-  |   ...
-  |   #9 ./a.out(_ZStlsIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_St17basic_string_viewIS3_S4_E+0x45) [0x55585c0afa55]
-  |   #10 ./a.out(main+0x9f) [0x55585c0af7cf]
-  |   #11 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
-  |   #12 ./a.out(_start+0x2a) [0x55585c0867ba]
-  |
-  | 0x7feccab26000 was deallocated by thread 31027 here:
-  |   ...
-  |   #7 ./a.out(main+0x83) [0x55585c0af7b3]
-  |   #8 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
-  |   #9 ./a.out(_start+0x2a) [0x55585c0867ba]
-  |
-  | 0x7feccab26000 was allocated by thread 31027 here:
-  |   ...
-  |   #12 ./a.out(main+0x57) [0x55585c0af787]
-  |   #13 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
-  |   #14 ./a.out(_start+0x2a) [0x55585c0867ba]
-  |
-  | *** End GWP-ASan report ***
-  | Segmentation fault
+```console
+$ clang++ -fsanitize=scudo -g buggy_code.cpp
+$ for i in `seq 1 500`; do
+    SCUDO_OPTIONS="GWP_ASAN_SampleRate=100" ./a.out > /dev/null;
+  done
+|
+| *** GWP-ASan detected a memory error ***
+| Use after free at 0x7feccab26000 (0 bytes into a 41-byte allocation at 0x7feccab26000) by thread 31027 here:
+|   ...
+|   #9 ./a.out(_ZStlsIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_St17basic_string_viewIS3_S4_E+0x45) [0x55585c0afa55]
+|   #10 ./a.out(main+0x9f) [0x55585c0af7cf]
+|   #11 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
+|   #12 ./a.out(_start+0x2a) [0x55585c0867ba]
+|
+| 0x7feccab26000 was deallocated by thread 31027 here:
+|   ...
+|   #7 ./a.out(main+0x83) [0x55585c0af7b3]
+|   #8 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
+|   #9 ./a.out(_start+0x2a) [0x55585c0867ba]
+|
+| 0x7feccab26000 was allocated by thread 31027 here:
+|   ...
+|   #12 ./a.out(main+0x57) [0x55585c0af787]
+|   #13 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
+|   #14 ./a.out(_start+0x2a) [0x55585c0867ba]
+|
+| *** End GWP-ASan report ***
+| Segmentation fault
+```
 
 To symbolize these stack traces, some care has to be taken. Scudo currently uses
-GNU's ``backtrace_symbols()`` from ``<execinfo.h>`` to unwind. The unwinder
-provides human-readable stack traces in ``function+offset`` form, rather than
-the normal ``binary+offset`` form. In order to use addr2line or similar tools to
-recover the exact line number, we must convert the ``function+offset`` to
-``binary+offset``. A helper script is available at
-``compiler-rt/lib/gwp_asan/scripts/symbolize.sh``. Using this script will
+GNU's `backtrace_symbols()` from `<execinfo.h>` to unwind. The unwinder
+provides human-readable stack traces in `function+offset` form, rather than
+the normal `binary+offset` form. In order to use addr2line or similar tools to
+recover the exact line number, we must convert the `function+offset` to
+`binary+offset`. A helper script is available at
+`compiler-rt/lib/gwp_asan/scripts/symbolize.sh`. Using this script will
 attempt to symbolize each possible line, falling back to the previous output if
 anything fails. This results in the following output:
 
-.. code:: console
-
-
-  $ cat my_gwp_asan_error.txt | symbolize.sh
-  |
-  | *** GWP-ASan detected a memory error ***
-  | Use after free at 0x7feccab26000 (0 bytes into a 41-byte allocation at 0x7feccab26000) by thread 31027 here:
-  | ...
-  | #9 /usr/lib/gcc/x86_64-linux-gnu/8.0.1/../../../../include/c++/8.0.1/string_view:547
-  | #10 /tmp/buggy_code.cpp:8
-  |
-  | 0x7feccab26000 was deallocated by thread 31027 here:
-  | ...
-  | #7 /tmp/buggy_code.cpp:8
-  | #8 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
-  | #9 ./a.out(_start+0x2a) [0x55585c0867ba]
-  |
-  | 0x7feccab26000 was allocated by thread 31027 here:
-  | ...
-  | #12 /tmp/buggy_code.cpp:7
-  | #13 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
-  | #14 ./a.out(_start+0x2a) [0x55585c0867ba]
-  |
-  | *** End GWP-ASan report ***
-  | Segmentation fault
+```console
+$ cat my_gwp_asan_error.txt | symbolize.sh
+|
+| *** GWP-ASan detected a memory error ***
+| Use after free at 0x7feccab26000 (0 bytes into a 41-byte allocation at 0x7feccab26000) by thread 31027 here:
+| ...
+| #9 /usr/lib/gcc/x86_64-linux-gnu/8.0.1/../../../../include/c++/8.0.1/string_view:547
+| #10 /tmp/buggy_code.cpp:8
+|
+| 0x7feccab26000 was deallocated by thread 31027 here:
+| ...
+| #7 /tmp/buggy_code.cpp:8
+| #8 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
+| #9 ./a.out(_start+0x2a) [0x55585c0867ba]
+|
+| 0x7feccab26000 was allocated by thread 31027 here:
+| ...
+| #12 /tmp/buggy_code.cpp:7
+| #13 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fecc966952b]
+| #14 ./a.out(_start+0x2a) [0x55585c0867ba]
+|
+| *** End GWP-ASan report ***
+| Segmentation fault
+```
+
diff --git a/llvm/docs/HowToAddABuilder.md b/llvm/docs/HowToAddABuilder.md
index bbc87e5841ddc..b7b5483a11193 100644
--- a/llvm/docs/HowToAddABuilder.md
+++ b/llvm/docs/HowToAddABuilder.md
@@ -1,28 +1,26 @@
-===================================================================
-How To Add Your Build Configuration To LLVM Buildbot Infrastructure
-===================================================================
+# How To Add Your Build Configuration To LLVM Buildbot Infrastructure
 
-Introduction
-============
+## Introduction
 
 This document contains information about adding a build configuration and
 buildbot worker to the LLVM Buildbot Infrastructure.
 
-.. note:: The term "buildmaster" is used in this document to refer to the
-  server that manages which builds are run and where. Though we would not
-  normally choose to use "master" terminology, it is used in this document
-  because it is the term that the Buildbot package currently
-  `uses <https://github.com/buildbot/buildbot/issues/5382>`_.
+:::{note}
+The term "buildmaster" is used in this document to refer to the
+server that manages which builds are run and where. Though we would not
+normally choose to use "master" terminology, it is used in this document
+because it is the term that the Buildbot package currently
+[uses](https://github.com/buildbot/buildbot/issues/5382).
+:::
 
-Buildmasters
-============
+## Buildmasters
 
 There are two buildmasters running.
 
-* The main buildmaster at `<https://lab.llvm.org/buildbot>`_. All builders
+- The main buildmaster at [https://lab.llvm.org/buildbot](https://lab.llvm.org/buildbot). All builders
   attached to this machine will notify commit authors every time they break
   the build.
-* The staging buildmaster at `<https://lab.llvm.org/staging>`_. All builders
+- The staging buildmaster at [https://lab.llvm.org/staging](https://lab.llvm.org/staging). All builders
   attached to this machine will be completely silent by default when the build
   is broken. This buildmaster is reconfigured every two hours with any new
   commits from the llvm-zorg repository.
@@ -30,239 +28,235 @@ There are two buildmasters running.
 In order to remain connected to the main buildmaster (and thus notify
 developers of failures), a buildbot must:
 
-* Be building a supported configuration.  Builders for experimental backends
+- Be building a supported configuration. Builders for experimental backends
   should generally be attached to staging buildmaster.
-* Be able to keep up with new commits to the main branch, or at a minimum
+- Be able to keep up with new commits to the main branch, or at a minimum
   recover to tip of tree within a couple of days of falling behind.
 
 Additionally, we encourage all bot owners to point their bots towards the
 staging master during maintenance windows, instability troubleshooting, and
 such.
 
-Roles & Expectations
-====================
+## Roles & Expectations
 
 Each buildbot has an owner who is the responsible party for addressing problems
-which arise with said buildbot.  We generally expect the bot owner to be
+which arise with said buildbot. We generally expect the bot owner to be
 reasonably responsive.
 
 For some bots, the ownership responsibility is split between a "resource owner"
 who provides the underlying machine resource, and a "configuration owner" who
-maintains the build configuration.  Generally, operational responsibility lies
-with the "config owner".  We do expect "resource owners" - who are generally
+maintains the build configuration. Generally, operational responsibility lies
+with the "config owner". We do expect "resource owners" - who are generally
 the contact listed in a workers attributes - to proxy requests to the relevant
 "config owner" in a timely manner.
 
 Most issues with a buildbot should be addressed directly with a bot owner
-via email.  Please CC `Galina Kistanova <mailto:gkistanova at gmail.com>`_.
+via email. Please CC [Galina Kistanova](mailto:gkistanova at gmail.com).
+
+## Steps To Add Builder To LLVM Buildbot
 
-Steps To Add Builder To LLVM Buildbot
-=====================================
 Volunteers can provide their build machines to work as build workers to
 public LLVM Buildbot.
 
 Here are the steps you can follow to do so:
 
-#. Check the existing build configurations to make sure the one you are
-   interested in is not covered yet or gets built on your computer much
-   faster than on the existing one. We prefer faster builds so developers
-   will get feedback sooner after changes get committed.
-
-#. The computer you will be registering with the LLVM buildbot
-   infrastructure should have all dependencies installed and be able to
-   build your configuration successfully. Please check what degree
-   of parallelism (-j param) would give the fastest build.  You can build
-   multiple configurations on one computer.
-
-#. Install buildbot-worker (currently we are using buildbot version 3.11.7).
-   This specific version can be installed using ``pip``, with a command such
-   as ``pip3 install buildbot-worker==3.11.7``.
-
-#. Create a designated user account, your buildbot-worker will be running under,
-   and set appropriate permissions.
-
-#. Choose the buildbot-worker root directory (all builds will be placed under
-   it), buildbot-worker access name and password the build master will be using
-   to authenticate your buildbot-worker.
-
-#. Create a buildbot-worker in context of that buildbot-worker account. Point it
-   to the **lab.llvm.org** port **9994** (see `Buildbot documentation,
-   Creating a worker
-   <http://docs.buildbot.net/current/tutorial/firstrun.html#creating-a-worker>`_
-   for more details) by running the following command:
-
-    .. code-block:: bash
-
-       $ buildbot-worker create-worker <buildbot-worker-root-directory> \
-                    lab.llvm.org:9994 \
-                    <buildbot-worker-access-name> \
-                    <buildbot-worker-access-password>
-
-   Only once a new worker is stable, and
-   approval from Galina has been received (see last step) should it
-   be pointed at the main buildmaster.
-
-   Now start the worker:
-
-    .. code-block:: bash
-
-       $ buildbot-worker start <buildbot-worker-root-directory>
-
-   This will cause your new worker to connect to the staging buildmaster
-   which is silent by default.
-
-   Try this once then check the log file
-   ``<buildbot-worker-root-directory>/worker/twistd.log``. If your settings
-   are correct you will see a refused connection. This is good and expected,
-   as the credentials have not been established on both ends. Now stop the
-   worker and proceed to the next steps.
-
-#. Fill the buildbot-worker description and admin name/e-mail.  Here is an
-   example of the buildbot-worker description::
-
-       Windows 7 x64
-       Core i7 (2.66GHz), 16GB of RAM
-
-       g++.exe (TDM-1 mingw32) 4.4.0
-       GNU Binutils 2.19.1
-       cmake version 2.8.4
-       Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
-
-   See `here <http://docs.buildbot.net/current/manual/installation/worker.html>`_
-   for which files to edit.
-
-#. Send a patch which adds your build worker and your builder to
-   `zorg <https://github.com/llvm/llvm-zorg>`_. Use the typical LLVM
-   `workflow <https://llvm.org/docs/Contributing.html#how-to-submit-a-patch>`_.
-
-   * workers are added to ``buildbot/osuosl/master/config/workers.py``
-   * builders are added to ``buildbot/osuosl/master/config/builders.py``
-
-   Please make sure your builder name and its builddir are unique through the
-   file.
-
-   All new builders should default to using the "'collapseRequests': False"
-   configuration.  This causes the builder to build each commit individually
-   and not merge build requests.  To maximize quality of feedback to developers,
-   we *strongly prefer* builders to be configured not to collapse requests.
-   This flag should be removed only after all reasonable efforts have been
-   exhausted to improve build times such that the builder can keep up with
-   commit flow.
-
-   It is possible to allow email addresses to unconditionally receive
-   notifications on build failure; for this you'll need to add an
-   ``InformativeMailNotifier`` to ``buildbot/osuosl/master/config/status.py``.
-   This is particularly useful for the staging buildmaster which is silent
-   otherwise.
-
-#. Send the buildbot-worker access name and the access password directly to
-   `Galina Kistanova <mailto:gkistanova at gmail.com>`_, and wait until she
-   lets you know that your changes are applied and buildmaster is
-   reconfigured.
-
-#. Make sure you can start the buildbot-worker and successfully connect
-   to the silent buildmaster. Then set up your buildbot-worker to start
-   automatically at the start up time.  See the buildbot documentation
-   for help.  You may want to restart your computer to see if it works.
-
-#. Check the status of your buildbot-worker on the `Waterfall Display (Staging)
-   <http://lab.llvm.org/staging/#/waterfall>`_ to make sure it is
-   connected, and the `Workers Display (Staging)
-   <http://lab.llvm.org/staging/#/workers>`_ to see if administrator
-   contact and worker information are correct.
-
-#. At this point, you have a working builder connected to the staging
-   buildmaster.  You can now make sure it is reliably green and keeps
-   up with the build queue.  No notifications will be sent, so you can
-   keep an unstable builder connected to staging indefinitely.
-
-#. (Optional) Once the builder is stable on the staging buildmaster with
-   several days of green history, you can choose to move it to the production
-   buildmaster to enable developer notifications.  Please email `Galina
-   Kistanova <mailto:gkistanova at gmail.com>`_ for review and approval.
-
-   To move a worker to production (once approved), stop your worker, edit the
-   buildbot.tac file to change the port number from 9994 to 9990 and start it
-   again.
-
-Testing a Builder Config Locally
-================================
+01. Check the existing build configurations to make sure the one you are
+    interested in is not covered yet or gets built on your computer much
+    faster than on the existing one. We prefer faster builds so developers
+    will get feedback sooner after changes get committed.
+
+02. The computer you will be registering with the LLVM buildbot
+    infrastructure should have all dependencies installed and be able to
+    build your configuration successfully. Please check what degree
+    of parallelism (-j param) would give the fastest build. You can build
+    multiple configurations on one computer.
+
+03. Install buildbot-worker (currently we are using buildbot version 3.11.7).
+    This specific version can be installed using `pip`, with a command such
+    as `pip3 install buildbot-worker==3.11.7`.
+
+04. Create a designated user account, your buildbot-worker will be running under,
+    and set appropriate permissions.
+
+05. Choose the buildbot-worker root directory (all builds will be placed under
+    it), buildbot-worker access name and password the build master will be using
+    to authenticate your buildbot-worker.
+
+06. Create a buildbot-worker in context of that buildbot-worker account. Point it
+    to the **lab.llvm.org** port **9994** (see [Buildbot documentation,
+    Creating a worker](http://docs.buildbot.net/current/tutorial/firstrun.html#creating-a-worker)
+    for more details) by running the following command:
+
+    > ```bash
+    > $ buildbot-worker create-worker <buildbot-worker-root-directory> \
+    >              lab.llvm.org:9994 \
+    >              <buildbot-worker-access-name> \
+    >              <buildbot-worker-access-password>
+    > ```
+
+    Only once a new worker is stable, and
+    approval from Galina has been received (see last step) should it
+    be pointed at the main buildmaster.
+
+    Now start the worker:
+
+    > ```bash
+    > $ buildbot-worker start <buildbot-worker-root-directory>
+    > ```
+
+    This will cause your new worker to connect to the staging buildmaster
+    which is silent by default.
+
+    Try this once then check the log file
+    `<buildbot-worker-root-directory>/worker/twistd.log`. If your settings
+    are correct you will see a refused connection. This is good and expected,
+    as the credentials have not been established on both ends. Now stop the
+    worker and proceed to the next steps.
+
+07. Fill the buildbot-worker description and admin name/e-mail. Here is an
+    example of the buildbot-worker description:
+
+    ```
+    Windows 7 x64
+    Core i7 (2.66GHz), 16GB of RAM
+
+    g++.exe (TDM-1 mingw32) 4.4.0
+    GNU Binutils 2.19.1
+    cmake version 2.8.4
+    Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
+    ```
+
+    See [here](http://docs.buildbot.net/current/manual/installation/worker.html)
+    for which files to edit.
+
+08. Send a patch which adds your build worker and your builder to
+    [zorg](https://github.com/llvm/llvm-zorg). Use the typical LLVM
+    [workflow](https://llvm.org/docs/Contributing.html#how-to-submit-a-patch).
+
+    - workers are added to `buildbot/osuosl/master/config/workers.py`
+    - builders are added to `buildbot/osuosl/master/config/builders.py`
+
+    Please make sure your builder name and its builddir are unique through the
+    file.
+
+    All new builders should default to using the "'collapseRequests': False"
+    configuration. This causes the builder to build each commit individually
+    and not merge build requests. To maximize quality of feedback to developers,
+    we *strongly prefer* builders to be configured not to collapse requests.
+    This flag should be removed only after all reasonable efforts have been
+    exhausted to improve build times such that the builder can keep up with
+    commit flow.
+
+    It is possible to allow email addresses to unconditionally receive
+    notifications on build failure; for this you'll need to add an
+    `InformativeMailNotifier` to `buildbot/osuosl/master/config/status.py`.
+    This is particularly useful for the staging buildmaster which is silent
+    otherwise.
+
+09. Send the buildbot-worker access name and the access password directly to
+    [Galina Kistanova](mailto:gkistanova at gmail.com), and wait until she
+    lets you know that your changes are applied and buildmaster is
+    reconfigured.
+
+10. Make sure you can start the buildbot-worker and successfully connect
+    to the silent buildmaster. Then set up your buildbot-worker to start
+    automatically at the start up time. See the buildbot documentation
+    for help. You may want to restart your computer to see if it works.
+
+11. Check the status of your buildbot-worker on the [Waterfall Display (Staging)](http://lab.llvm.org/staging/#/waterfall) to make sure it is
+    connected, and the [Workers Display (Staging)](http://lab.llvm.org/staging/#/workers) to see if administrator
+    contact and worker information are correct.
+
+12. At this point, you have a working builder connected to the staging
+    buildmaster. You can now make sure it is reliably green and keeps
+    up with the build queue. No notifications will be sent, so you can
+    keep an unstable builder connected to staging indefinitely.
+
+13. (Optional) Once the builder is stable on the staging buildmaster with
+    several days of green history, you can choose to move it to the production
+    buildmaster to enable developer notifications. Please email [Galina
+    Kistanova](mailto:gkistanova at gmail.com) for review and approval.
+
+    To move a worker to production (once approved), stop your worker, edit the
+    buildbot.tac file to change the port number from 9994 to 9990 and start it
+    again.
+
+## Testing a Builder Config Locally
 
 It is possible to test a builder running against a local version of LLVM's
 buildmaster setup. This allows you to test changes to builder, worker, and
 buildmaster configuration. A buildmaster launched in this "local testing" mode
 will:
 
-* Bind only to local interfaces.
-* Use SQLite as the database.
-* Use a single fixed password for workers.
-* Disable extras like GitHub authentication.
+- Bind only to local interfaces.
+- Use SQLite as the database.
+- Use a single fixed password for workers.
+- Disable extras like GitHub authentication.
 
 In order to use this "local testing" mode:
 
-* Create and activate a Python `venv
-  <https://docs.python.org/3/library/venv.html>`_ and install the necessary
+- Create and activate a Python [venv](https://docs.python.org/3/library/venv.html) and install the necessary
   dependencies. This step can be run from any directory.
 
-    .. code-block:: bash
+  > ```bash
+  > python -m venv bbenv
+  > source bbenv/bin/activate
+  > pip install buildbot{,-console-view,-grid-view,-waterfall-view,-worker,-www}==3.11.7 urllib3
+  > ```
 
-       python -m venv bbenv
-       source bbenv/bin/activate
-       pip install buildbot{,-console-view,-grid-view,-waterfall-view,-worker,-www}==3.11.7 urllib3
-
-* If your system has Python 3.13 or newer you will need to additionally
-  install ``legacy-cgi`` and make a minor patch to the installed buildbot
+- If your system has Python 3.13 or newer you will need to additionally
+  install `legacy-cgi` and make a minor patch to the installed buildbot
   package. This step does not need to be followed for earlier Python versions.
 
-    .. code-block:: bash
-
-       pip install legacy-cgi
-       sed -i \
-         -e 's/import pipes/import shlex/' \
-         -e 's/pipes\.quote/shlex.quote/' \
-         bbenv/lib/python3.13/site-packages/buildbot_worker/runprocess.py
-
-* Initialise the necessary buildmaster files, link to the configuration in a
-  local checkout out of `llvm-zorg <https://github.com/llvm/llvm-zorg>`_, and
-  ask ``buildbot`` to check the configuration. This step can be run from any
+  > ```bash
+  > pip install legacy-cgi
+  > sed -i \
+  >   -e 's/import pipes/import shlex/' \
+  >   -e 's/pipes\.quote/shlex.quote/' \
+  >   bbenv/lib/python3.13/site-packages/buildbot_worker/runprocess.py
+  > ```
+
+- Initialise the necessary buildmaster files, link to the configuration in a
+  local checkout out of [llvm-zorg](https://github.com/llvm/llvm-zorg), and
+  ask `buildbot` to check the configuration. This step can be run from any
   directory.
 
-    .. code-block:: bash
-
-       buildbot create-master llvm-testbbmaster
-       cd llvm-testbbmaster
-       ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/master.cfg .
-       ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/config/ .
-       ln -s /path/to/checkout/of/llvm-zorg/zorg/ .
-       BUILDBOT_TEST=1 buildbot checkconfig
-
-* Start the buildmaster.
+  > ```bash
+  > buildbot create-master llvm-testbbmaster
+  > cd llvm-testbbmaster
+  > ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/master.cfg .
+  > ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/config/ .
+  > ln -s /path/to/checkout/of/llvm-zorg/zorg/ .
+  > BUILDBOT_TEST=1 buildbot checkconfig
+  > ```
 
-    .. code-block:: bash
+- Start the buildmaster.
 
-       BUILDBOT_TEST=1 buildbot start --nodaemon .
+  > ```bash
+  > BUILDBOT_TEST=1 buildbot start --nodaemon .
+  > ```
 
-* After waiting a few seconds for startup to complete, you should be able to
-  open the web UI at ``http://localhost:8011``.  If there are any errors or
-  this isn't working, check ``twistd.log`` (within the current directory) for
+- After waiting a few seconds for startup to complete, you should be able to
+  open the web UI at `http://localhost:8011`. If there are any errors or
+  this isn't working, check `twistd.log` (within the current directory) for
   more information.
 
-* You can now create and start a buildbot worker. Ensure you pick the correct
+- You can now create and start a buildbot worker. Ensure you pick the correct
   name for the worker associated with the build configuration you want to test
-  in ``buildbot/osuosl/master/config/builders.py``.
+  in `buildbot/osuosl/master/config/builders.py`.
 
-    .. code-block:: bash
+  > ```bash
+  > buildbot-worker create-worker <buildbot-worker-root-directory> \
+  >                 localhost:9990 \
+  >                 <buildbot-worker-name> \
+  >                 test
+  > buildbot-worker start --nodaemon <buildbot-worker-root-directory>
+  > ```
 
-       buildbot-worker create-worker <buildbot-worker-root-directory> \
-                       localhost:9990 \
-                       <buildbot-worker-name> \
-                       test
-       buildbot-worker start --nodaemon <buildbot-worker-root-directory>
-
-* Either wait until the poller sets off a build, or alternatively force a
+- Either wait until the poller sets off a build, or alternatively force a
   build to start in the web UI.
 
-* Review the progress and results of the build in the web UI.
+- Review the progress and results of the build in the web UI.
 
 This local testing configuration defaults to binding only to the loopback
 interface for security reasons.
@@ -271,87 +265,91 @@ If you want to run the test worker on a different machine, or to run the
 buildmaster on a remote server, ssh port forwarding can be used to make
 connection possible. For instance, if running the buildmaster on a remote
 server the following command will suffice to make the web UI accessible via
-``http://localhost:8011`` and make it possible for a local worker to connect
-to the remote buildmaster by connecting to ``localhost:9900``:
-
-    .. code-block:: bash
+`http://localhost:8011` and make it possible for a local worker to connect
+to the remote buildmaster by connecting to `localhost:9900`:
 
-       ssh -N -L 8011:localhost:8011 -L 9990:localhost:9990 username at buildmaster_server_address
+> ```bash
+> ssh -N -L 8011:localhost:8011 -L 9990:localhost:9990 username at buildmaster_server_address
+> ```
 
 Be aware that some build configurations may checkout the current upstream
-``llvm-zorg`` repository in order to retrieve additional scripts used during
+`llvm-zorg` repository in order to retrieve additional scripts used during
 the build process, meaning any local changes will not be reflected in this
 part of the build. If you wish to test changes to any of these scripts without
 committing them upstream, you will need to temporarily patch the builder logic
 in order to instead check out your own branch.
-Typically, ``addGetSourcecodeForProject`` from
-``zorg/buildbot/process/factory.py`` is used for this and you can edit the
-caller to specify your own ``repourl`` and/or ``branch`` keyword argument.
+Typically, `addGetSourcecodeForProject` from
+`zorg/buildbot/process/factory.py` is used for this and you can edit the
+caller to specify your own `repourl` and/or `branch` keyword argument.
 
-Best Practices for Configuring a Fast Builder
-=============================================
+## Best Practices for Configuring a Fast Builder
 
 As mentioned above, we generally have a strong preference for
-builders which can build every commit as they come in.  This section
+builders which can build every commit as they come in. This section
 includes best practices and some recommendations as to how to achieve
 that end.
 
 The goal
-  In 2020, the monorepo had just under 35 thousand commits.  This works
-  out to an average of 4 commits per hour.  Already, we can see that a
+
+: In 2020, the monorepo had just under 35 thousand commits. This works
+  out to an average of 4 commits per hour. Already, we can see that a
   builder must cycle in less than 15 minutes to have a hope of being
-  useful.  However, those commits are not uniformly distributed.  They
-  tend to cluster strongly during US working hours.  Looking at a couple
+  useful. However, those commits are not uniformly distributed. They
+  tend to cluster strongly during US working hours. Looking at a couple
   of recent (Nov 2021) working days, we routinely see ~10 commits per
   hour during peek times, with occasional spikes as high as ~15 commits
-  per hour.  Thus, as a rule of thumb, we should plan for our builder to
+  per hour. Thus, as a rule of thumb, we should plan for our builder to
   complete ~10-15 builds an hour.
 
 Resource Appropriately
-  At 10-15 builds per hour, we need to complete a new build on average every
-  4 to 6 minutes.  For anything except the fastest of hardware/build configs,
-  this is going to be well beyond the ability of a single machine.  In buildbot
+
+: At 10-15 builds per hour, we need to complete a new build on average every
+  4 to 6 minutes. For anything except the fastest of hardware/build configs,
+  this is going to be well beyond the ability of a single machine. In buildbot
   terms, we likely going to need multiple workers to build requests in parallel
-  under a single builder configuration.  For some rough back of the envelope
+  under a single builder configuration. For some rough back of the envelope
   numbers, if your build config takes e.g. 30 minutes, you will need something
-  on the order of 5-8 workers.  If your build config takes ~2 hours, you'll
-  need something on the order of 20-30 workers.  The rest of this section
+  on the order of 5-8 workers. If your build config takes ~2 hours, you'll
+  need something on the order of 20-30 workers. The rest of this section
   focuses on how to reduce cycle times.
 
 Restrict what you build and test
-  Think hard about why you're setting up a bot, and restrict your build
-  configuration as much as you can.  Basic functionality is probably
+
+: Think hard about why you're setting up a bot, and restrict your build
+  configuration as much as you can. Basic functionality is probably
   already covered by other bots, and you don't need to duplicate that
-  testing.  You only need to be building and testing the *unique* parts
-  of the configuration.  (e.g. For a multi-stage clang builder, you probably
+  testing. You only need to be building and testing the *unique* parts
+  of the configuration. (e.g. For a multi-stage clang builder, you probably
   don't need to be enabling every target or building all the various utilities.)
 
   It can sometimes be worthwhile splitting a single builder into two or more,
-  if you have multiple distinct purposes for the same builder.  As an example,
+  if you have multiple distinct purposes for the same builder. As an example,
   if you want to both a) confirm that all of LLVM builds with your host
   compiler, and b) want to do a multi-stage clang build on your target, you
-  may be better off with two separate bots.  Splitting increases resource
+  may be better off with two separate bots. Splitting increases resource
   consumption, but makes it easy for each bot to keep up with commit flow.
   Additionally, splitting bots may assist in triage by narrowing attention to
   relevant parts of the failing configuration.
 
-  In general, we recommend Release build types with Assertions enabled.  This
+  In general, we recommend Release build types with Assertions enabled. This
   generally provides a good balance between build times and bug detection for
-  most buildbots.  There may be room for including some debug info (e.g. with
+  most buildbots. There may be room for including some debug info (e.g. with
   `-gmlt`), but in general the balance between debug info quality and build
   times is a delicate one.
 
 Use Ninja & LLD
-  Ninja really does help build times over Make, particularly for highly
-  parallel builds.  LLD helps to reduce both link times and memory usage
-  during linking significantly.  With a build machine with sufficient
+
+: Ninja really does help build times over Make, particularly for highly
+  parallel builds. LLD helps to reduce both link times and memory usage
+  during linking significantly. With a build machine with sufficient
   parallelism, link times tend to dominate critical path of the build, and are
   thus worth optimizing.
 
 Use CCache and NOT incremental builds
-  Using ccache materially improves average build times.  Incremental builds
+
+: Using ccache materially improves average build times. Incremental builds
   can be slightly faster, but introduce the risk of build corruption due to
-  e.g. state changes, etc...  At this point, the recommendation is not to
+  e.g. state changes, etc... At this point, the recommendation is not to
   use incremental builds and instead use ccache as the latter captures the
   majority of the benefit with less risk of false positives.
 
@@ -362,55 +360,57 @@ Use CCache and NOT incremental builds
   hit in cache and the build request will complete in just the testing time.
 
   With multiple workers, it is tempting to try to configure a shared cache
-  between the workers.  Experience to date indicates this is difficult to
+  between the workers. Experience to date indicates this is difficult to
   well, and that having local per-worker caches gets most of the benefit
-  anyways.  We don't currently recommend shared caches.
+  anyways. We don't currently recommend shared caches.
 
   CCache does depend on the builder hardware having sufficient IO to access
   the cache with reasonable access times - i.e. a fast disk, or enough memory
-  for a RAM cache, etc..  For builders without, incremental may be your best
+  for a RAM cache, etc.. For builders without, incremental may be your best
   option, but is likely to require higher ongoing involvement from the
   sponsor.
 
 Enable batch builds
-  As a last resort, you can configure your builder to batch build requests.
+
+: As a last resort, you can configure your builder to batch build requests.
   This makes the build failure notifications markedly less actionable, and
   should only be done once all other reasonable measures have been taken.
 
 Leave it on the staging buildmaster
-  While most of this section has been biased towards builders intended for
+
+: While most of this section has been biased towards builders intended for
   the main buildmaster, it is worth highlighting that builders can run
-  indefinitely on the staging buildmaster.  Such a builder may still be
+  indefinitely on the staging buildmaster. Such a builder may still be
   useful for the sponsoring organization, without concern of negatively
-  impacting the broader community.  The sponsoring organization simply
+  impacting the broader community. The sponsoring organization simply
   has to take on the responsibility of all bisection and triage.
 
-Managing a Worker From The Web Interface
-========================================
+## Managing a Worker From The Web Interface
 
 Tasks such as clearing pending building requests can be done using
 the Buildbot web interface. To do this you must be recognised as an admin
 of the worker:
 
-* Set your public GitHub profile email to one that was included in the
-  ``admin`` information you set up on the worker. It does not matter if this
+- Set your public GitHub profile email to one that was included in the
+  `admin` information you set up on the worker. It does not matter if this
   is your primary account email or a "verified email". To confirm this has been
-  done correctly, go to ``github.com/<your GitHub username>`` and you should
+  done correctly, go to `github.com/<your GitHub username>` and you should
   see the email address listed there.
 
   A worker can have many admins, if they are listed in the form
-  ``First Last <first.last at example.com>, First2 Last2 <first2.last2 at example.com>``.
+  `First Last <first.last at example.com>, First2 Last2 <first2.last2 at example.com>`.
   You only need to have one of those addresses in your profile to be recognised
   as an admin.
 
-  If you need to add an email address, you can edit the ``admin`` file and
+  If you need to add an email address, you can edit the `admin` file and
   restart the worker. You should see the new admin details in the web interface
   shortly afterwards.
 
-* Connect GitHub to Buildbot by clicking on the "Anonymous" button on the
+- Connect GitHub to Buildbot by clicking on the "Anonymous" button on the
   top right of the page, then "Login with GitHub" and authorise the app.
 
 Some tasks don't give immediate feedback, so if nothing happens within a short
 time, try again with the browser's web console open. Sometimes you will see
 403 errors and other messages that might indicate you don't have the correct
 details set up.
+
diff --git a/llvm/docs/HowToReleaseLLVM.md b/llvm/docs/HowToReleaseLLVM.md
index b374b0dd8cd3d..62bd66d5876da 100644
--- a/llvm/docs/HowToReleaseLLVM.md
+++ b/llvm/docs/HowToReleaseLLVM.md
@@ -1,217 +1,201 @@
-=================================
-How To Release LLVM To The Public
-=================================
+# How To Release LLVM To The Public
 
-Introduction
-============
+## Introduction
 
 This document contains information about successfully releasing LLVM ---
-including sub-projects: e.g., ``clang`` and ``compiler-rt`` --- to the public.
+including sub-projects: e.g., `clang` and `compiler-rt` --- to the public.
 It is the Release Manager's responsibility to ensure that a high quality build
 of LLVM is released.
 
 If you're looking for the document on how to test the release candidates and
-create the binary packages, please refer to the :doc:`ReleaseProcess` instead.
+create the binary packages, please refer to the {doc}`ReleaseProcess` instead.
 
-.. _timeline:
+(timeline)=
 
-Release Timeline
-================
+## Release Timeline
 
 LLVM is released on a time-based schedule --- with major releases roughly
-every 6 months.  In between major releases there may be dot releases.
+every 6 months. In between major releases there may be dot releases.
 The release manager will determine if and when to make a dot release based
-on feedback from the community.  Typically, dot releases should be made if
+on feedback from the community. Typically, dot releases should be made if
 there are a large number of bug fixes in the stable branch or a critical bug
 has been discovered that affects a large number of users.
 
 Unless otherwise stated, dot releases will follow the same procedure as
 major releases.
 
-Annual Release Schedule
------------------------
+### Annual Release Schedule
 
-Here is the annual release schedule for LLVM.  This is meant to be a
+Here is the annual release schedule for LLVM. This is meant to be a
 guide, and release managers are not required to follow this exactly.
 Releases should be tagged on Tuesdays.
 
-=============================== =========================
-Release                         Approx. Date
-=============================== =========================
-*release branch: even releases* *2nd Tue in January*
-*release branch: odd releases*  *2nd Tue in July*
-X.1.0-rc1                       3 days after branch.
-X.1.0-rc2                       2 weeks after branch.
-X.1.0-rc3                       4 weeks after branch
-**X.1.0-final**                 **6 weeks after branch**
-**X.1.1**                       **8 weeks after branch**
-**X.1.2**                       **10 weeks after branch**
-**X.1.3**                       **12 weeks after branch**
-**X.1.4**                       **14 weeks after branch**
-**X.1.5**                       **16 weeks after branch**
-**X.1.6**                       **18 weeks after branch**
-**X.1.7**                       **20 weeks after branch**
-**X.1.8**                       **22 weeks after branch**
-**X.1.9** (If necessary)        **24 weeks after branch**
-**Next release branches**       **~25 weeks after branch**
-=============================== =========================
-
-Release Process Summary
------------------------
-
-* Announce release schedule to the LLVM community and update the website.  Do
+| Release                         | Approx. Date               |
+| ------------------------------- | -------------------------- |
+| *release branch: even releases* | *2nd Tue in January*       |
+| *release branch: odd releases*  | *2nd Tue in July*          |
+| X.1.0-rc1                       | 3 days after branch.       |
+| X.1.0-rc2                       | 2 weeks after branch.      |
+| X.1.0-rc3                       | 4 weeks after branch       |
+| **X.1.0-final**                 | **6 weeks after branch**   |
+| **X.1.1**                       | **8 weeks after branch**   |
+| **X.1.2**                       | **10 weeks after branch**  |
+| **X.1.3**                       | **12 weeks after branch**  |
+| **X.1.4**                       | **14 weeks after branch**  |
+| **X.1.5**                       | **16 weeks after branch**  |
+| **X.1.6**                       | **18 weeks after branch**  |
+| **X.1.7**                       | **20 weeks after branch**  |
+| **X.1.8**                       | **22 weeks after branch**  |
+| **X.1.9** (If necessary)        | **24 weeks after branch**  |
+| **Next release branches**       | **~25 weeks after branch** |
+
+### Release Process Summary
+
+- Announce release schedule to the LLVM community and update the website. Do
   this at least 3 weeks before the -rc1 release.
-
-* Create release branch and begin release process.
-
-* Send out release candidate sources for first round of testing.  Testing lasts
-  6 weeks.  During the first round of testing, any regressions found should be
-  fixed.  Patches are merged from mainline into the release branch.  Also, all
-  features need to be completed during this time.  Any features not completed at
+- Create release branch and begin release process.
+- Send out release candidate sources for first round of testing. Testing lasts
+  6 weeks. During the first round of testing, any regressions found should be
+  fixed. Patches are merged from mainline into the release branch. Also, all
+  features need to be completed during this time. Any features not completed at
   the end of the first round of testing will be removed or disabled for the
   release.
+- Generate and send out the second release candidate sources. Only *critical*
+  bugs found during this testing phase will be fixed. Any bugs introduced by
+  merged patches will be fixed. If so, a third round of testing is needed.
+- The release notes are updated.
+- Finally, release!
+- Announce bug fix release schedule to the LLVM community and update the website.
+- Do bug-fix releases every two weeks until X.1.5 or X.1.6 (if necessary).
 
-* Generate and send out the second release candidate sources.  Only *critical*
-  bugs found during this testing phase will be fixed.  Any bugs introduced by
-  merged patches will be fixed.  If so, a third round of testing is needed.
-
-* The release notes are updated.
-
-* Finally, release!
-
-* Announce bug fix release schedule to the LLVM community and update the website.
+## Release Process
 
-* Do bug-fix releases every two weeks until X.1.5 or X.1.6 (if necessary).
-
-Release Process
-===============
-
-
-Release Administrative Tasks
-----------------------------
+### Release Administrative Tasks
 
 This section describes a few administrative tasks that need to be done for the
-release process to begin.  Specifically, it involves:
-
-* Updating version numbers,
+release process to begin. Specifically, it involves:
 
-* Creating the release branch, and
+- Updating version numbers,
+- Creating the release branch, and
+- Tagging release candidates for the release team to begin testing.
 
-* Tagging release candidates for the release team to begin testing.
-
-Create Release Branch and Update LLVM Version
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+#### Create Release Branch and Update LLVM Version
 
 Branch the Git trunk using the following procedure:
 
-#. Verify the current git trunk is in decent shape by examining the status
-   checks on the `most recent commits <https://github.com/llvm/llvm-project/commits/main>`_
+1. Verify the current git trunk is in decent shape by examining the status
+   checks on the [most recent commits](https://github.com/llvm/llvm-project/commits/main)
 
-#. Bump the version in trunk to ``N.0.0git`` and clear the release notes: ::
+2. Bump the version in trunk to `N.0.0git` and clear the release notes:
 
-    $ llvm/utils/release/bump-version.py --git N.0.0
-    $ llvm/utils/release/clear-release-notes.py
-    $ git commit -am "Bump version to N.0.0git"
-    $ git push origin main
+   ```
+   $ llvm/utils/release/bump-version.py --git N.0.0
+   $ llvm/utils/release/clear-release-notes.py
+   $ git commit -am "Bump version to N.0.0git"
+   $ git push origin main
+   ```
 
    If the push fails, rebase if necessary and try again.
 
-#. Tag the commit bumping the version on trunk with ``llvmorg-N-init``. If
-   ``X`` is the version to be released, then ``N`` is ``X + 1``. ::
+3. Tag the commit bumping the version on trunk with `llvmorg-N-init`. If
+   `X` is the version to be released, then `N` is `X + 1`.
 
-    $ GPG_TTY=$(tty) git tag -sa llvmorg-N-init -m "llvmorg-N-init"
-    $ git push origin llvmorg-N-init
+   ```
+   $ GPG_TTY=$(tty) git tag -sa llvmorg-N-init -m "llvmorg-N-init"
+   $ git push origin llvmorg-N-init
+   ```
 
-#. Create the release branch from the last known good revision before the
-   version bump. The branch name is ``release/X.x`` where ``X`` is the major
-   version number and ``x`` is just the letter ``x``. Assuming the revision
-   before the version bump is ok, this would be: ::
+4. Create the release branch from the last known good revision before the
+   version bump. The branch name is `release/X.x` where `X` is the major
+   version number and `x` is just the letter `x`. Assuming the revision
+   before the version bump is ok, this would be:
 
-    $ git checkout -b release/X.x llvmorg-N-init^
+   ```
+   $ git checkout -b release/X.x llvmorg-N-init^
+   ```
 
-#. On the newly-created release branch, immediately bump the version to
-   ``X.1.0git`` (where ``X`` is the major version number): ::
+5. On the newly-created release branch, immediately bump the version to
+   `X.1.0git` (where `X` is the major version number):
 
-    $ llvm/utils/release/bump-version.py --git X.1.0
-    $ git commit -am "Bump version to X.1.0git"
+   ```
+   $ llvm/utils/release/bump-version.py --git X.1.0
+   $ git commit -am "Bump version to X.1.0git"
+   ```
 
-#. Push release branch: ::
+6. Push release branch:
 
-    $ git push -u origin release/X.x
+   ```
+   $ git push -u origin release/X.x
+   ```
 
-#. And finally, create the release branch in ``llvm-test-suite`` repo: ::
+7. And finally, create the release branch in `llvm-test-suite` repo:
 
-    $ cd <llvm-test-suite>
-    $ git checkout main
-    $ git pull
-    $ git checkout -b release/X.x
-    $ git push -u origin release/X.x
+   ```
+   $ cd <llvm-test-suite>
+   $ git checkout main
+   $ git pull
+   $ git checkout -b release/X.x
+   $ git push -u origin release/X.x
+   ```
 
-Tagging the LLVM Release Candidates
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+#### Tagging the LLVM Release Candidates
 
 Tag release candidates:
 
-::
-
-  $ git tag -sa llvmorg-X.Y.Z-rcN
+```
+$ git tag -sa llvmorg-X.Y.Z-rcN
+```
 
 The pre-packaged source tarballs will be automatically generated via the
-`Release Sources
-<https://github.com/llvm/llvm-project/actions/workflows/release-sources.yml>`_
-workflow on GitHub.  This workflow will create an artifact containing all the
-release tarballs and the artifact attestation.  Additionally the `Release
-Documentation
-<https://github.com/llvm/llvm-project/actions/workflows/release-documentation.yml>`_
+[Release Sources](https://github.com/llvm/llvm-project/actions/workflows/release-sources.yml)
+workflow on GitHub. This workflow will create an artifact containing all the
+release tarballs and the artifact attestation. Additionally the [Release
+Documentation](https://github.com/llvm/llvm-project/actions/workflows/release-documentation.yml)
 workflow will run and create an artifact containing the man pages and the
 artifact attestation. The Release Manager should download the artifacts from
 both workflows, verify the tarballs, sign them, and then upload them to the
 release page.
 
-::
-
-  $ unzip artifact.zip
-  $ gh auth login
-  $ for f in *.xz; do gh attestation verify --owner llvm $f && gpg -b $f; done
-
-Tarballs, release binaries,  or any other release artifacts must be uploaded to
-GitHub.  This can be done using the ``github-upload-release.py`` script in ``utils/release``.
+```
+$ unzip artifact.zip
+$ gh auth login
+$ for f in *.xz; do gh attestation verify --owner llvm $f && gpg -b $f; done
+```
 
-::
+Tarballs, release binaries, or any other release artifacts must be uploaded to
+GitHub. This can be done using the `github-upload-release.py` script in `utils/release`.
 
-  $ github-upload-release.py upload --token <github-token> --release X.Y.Z-rcN --files <release_files>
+```
+$ github-upload-release.py upload --token <github-token> --release X.Y.Z-rcN --files <release_files>
+```
 
-
-Build The Binary Distribution
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+#### Build The Binary Distribution
 
 Creating the binary distribution requires following the instructions
-:doc:`here <ReleaseProcess>`.
+{doc}`here <ReleaseProcess>`.
 
 That process performs both Release+Asserts and Release builds but only packs
 the Release build for upload. You should use the Release+Asserts sysroot,
-normally under ``final/Phase3/Release+Asserts/llvmCore-3.8.1-RCn.install/``,
+normally under `final/Phase3/Release+Asserts/llvmCore-3.8.1-RCn.install/`,
 for test-suite and run-time benchmarks, to ensure nothing serious has
 passed through the net. For compile-time benchmarks, use the Release version.
 
-The minimum required version of the tools you'll need are :doc:`here <GettingStarted>`
+The minimum required version of the tools you'll need are {doc}`here <GettingStarted>`
 
-Release Qualification Criteria
-------------------------------
+### Release Qualification Criteria
 
 There are no official release qualification criteria.
-The release manager determines when a release is ready.  The release manager
+The release manager determines when a release is ready. The release manager
 should pay attention to the results of community testing, the number of outstanding
 bugs, and the number of regressions when determining whether or not to make a
 release.
 
 The community values time based releases, so releases should not be delayed for
-too long unless critical issues remain.  In most cases, the only
+too long unless critical issues remain. In most cases, the only
 kind of bugs that are critical enough to block a release would be a major regression
 from a previous release.
 
-Official Testing
-----------------
+### Official Testing
 
 A few developers in the community have dedicated time to validate the release
 candidates and volunteered to be the official release testers for each
@@ -227,43 +211,38 @@ release, all reported bugs will be deferred to the next stable release.
 
 The official release managers are:
 
-* Even releases: Tom Stellard (tstellar at redhat.com)
-* Odd releases: Tobias Hieta (tobias at hieta.se)
+- Even releases: Tom Stellard (<mailto:tstellar at redhat.com>)
+- Odd releases: Tobias Hieta (<mailto:tobias at hieta.se>)
 
 The official release testers are volunteers from the community who have
 consistently validated and released binaries for their targets/OSs. To contact
-them, you should post on the `Discourse forums (Project
-Infrastructure - Release Testers). <https://discourse.llvm.org/c/infrastructure/release-testers/66>`_
+them, you should post on the [Discourse forums (Project
+Infrastructure - Release Testers).](https://discourse.llvm.org/c/infrastructure/release-testers/66)
 
-The official testers list is in the file ``RELEASE_TESTERS.TXT``
-<https://github.com/llvm/llvm-project/blob/main/llvm/RELEASE_TESTERS.TXT>`_, in
+The official testers list is in the file `RELEASE_TESTERS.TXT`
+\<<https://github.com/llvm/llvm-project/blob/main/llvm/RELEASE_TESTERS.TXT>>\`\_, in
 the LLVM repository.
 
-Community Testing
------------------
+### Community Testing
 
 Once all testing is complete and appropriate bugs are filed, the release
 candidate tarballs are put on the website, and the LLVM community is notified.
 
 We ask that all LLVM developers test the release in any the following ways:
 
-#. Download ``llvm-X.Y``, ``llvm-test-X.Y``, and the appropriate ``clang``
-   binary.  Build LLVM.  Run ``make check`` and the full LLVM test suite (``make
-   TEST=nightly report``).
-
-#. Download ``llvm-X.Y``, ``llvm-test-X.Y``, and the ``clang`` sources.  Compile
-   everything.  Run ``make check`` and the full LLVM test suite (``make
-   TEST=nightly report``).
-
-#. Download ``llvm-X.Y``, ``llvm-test-X.Y``, and the appropriate ``clang``
+1. Download `llvm-X.Y`, `llvm-test-X.Y`, and the appropriate `clang`
+   binary. Build LLVM. Run `make check` and the full LLVM test suite (`make
+   TEST=nightly report`).
+2. Download `llvm-X.Y`, `llvm-test-X.Y`, and the `clang` sources. Compile
+   everything. Run `make check` and the full LLVM test suite (`make
+   TEST=nightly report`).
+3. Download `llvm-X.Y`, `llvm-test-X.Y`, and the appropriate `clang`
    binary. Build whole programs with it (ex. Chromium, Firefox, Apache) for
    your platform.
-
-#. Download ``llvm-X.Y``, ``llvm-test-X.Y``, and the appropriate ``clang``
+4. Download `llvm-X.Y`, `llvm-test-X.Y`, and the appropriate `clang`
    binary. Build *your* programs with it and check for conformance and
    performance regressions.
-
-#. Run the :doc:`release process <ReleaseProcess>`, if your platform is
+5. Run the {doc}`release process <ReleaseProcess>`, if your platform is
    *different* than that which is officially supported, and report back errors
    only if they were not reported by the official release tester for that
    architecture.
@@ -283,8 +262,7 @@ the time to solve additional and unrelated bugs!* If no patches are merged in,
 the release is determined to be ready and the release manager may move onto the
 next stage.
 
-Reporting Regressions
----------------------
+### Reporting Regressions
 
 Every regression found during the tests (as per the criteria above)
 should be filled in a bug in GitHub and added to the release milestone.
@@ -292,145 +270,126 @@ should be filled in a bug in GitHub and added to the release milestone.
 If a bug can't be reproduced or stops being a blocker, it should be removed
 from the Milestone. Debugging can continue, but on trunk.
 
-Backport Requests
------------------
+### Backport Requests
 
 Instructions for requesting a backport to a stable branch can be found
-:ref:`here <backporting>`.
+{ref}`here <backporting>`.
 
-Triaging Bug Reports for Releases
----------------------------------
+### Triaging Bug Reports for Releases
 
 This section describes how to triage bug reports:
 
-#. Search for bugs with a Release Milestone that have not been added to the
+1. Search for bugs with a Release Milestone that have not been added to the
    "Release Status" github project:
 
-   https://github.com/llvm/llvm-project/issues?q=is%3Aissue+milestone%3A%22LLVM+14.0.5+Release%22+no%3Aproject+
+   <https://github.com/llvm/llvm-project/issues?q=is%3Aissue+milestone%3A%22LLVM+14.0.5+Release%22+no%3Aproject+>
 
    Replace 14.0.5 in this query with the version from the Release Milestone being
    targeted.
 
    Add these bugs to the "Release Status" project.
 
-#. Navigate to the `Release Status project <https://github.com/orgs/llvm/projects/3>`_
+2. Navigate to the [Release Status project](https://github.com/orgs/llvm/projects/3)
    to see the list of bugs that are being considered for the release.
 
-#. Review each bug and first check if it has been fixed in main.  If it has, update
+3. Review each bug and first check if it has been fixed in main. If it has, update
    its status to "Needs Pull Request" and create a pull request for the fix
-   using the ``/cherry-pick`` or ``/branch`` comments if this has not been done already.
+   using the `/cherry-pick` or `/branch` comments if this has not been done already.
 
-#. If a bug has been fixed and has a pull request created for backporting it,
+4. If a bug has been fixed and has a pull request created for backporting it,
    then update its status to "Needs Review" and notify a knowledgeable
-   reviewer.  Usually you will want to notify the person who approved the
+   reviewer. Usually you will want to notify the person who approved the
    patch, but you may use your best judgement on who a good reviewer would be.
    Once you have identified the reviewer(s), assign the issue to them and
-   mention them (i.e., ``@username``) in a comment and ask them if the patch is safe
-   to backport.  You should also review the bug yourself to ensure that it
+   mention them (i.e., `@username`) in a comment and ask them if the patch is safe
+   to backport. You should also review the bug yourself to ensure that it
    meets the requirements for committing to the release branch.
 
-#. Once a bug has been reviewed, update the status to "Needs Merge". Check the
+5. Once a bug has been reviewed, update the status to "Needs Merge". Check the
    pull request associated with the issue. If all the tests pass, then the pull
    request can be merged. If not, then add a comment on the issue asking
    someone to take a look at the failures.
 
-
-Release Patch Rules
--------------------
+### Release Patch Rules
 
 Below are the rules regarding patching the release branch:
 
-#. Patches applied to the release branch may only be applied by the release
+1. Patches applied to the release branch may only be applied by the release
    manager, the official release testers, or the maintainers with approval from
    the release manager.
-
-#. Release managers are encouraged, but not required, to get approval from a
-   maintainer before approving patches.  If there are no reachable maintainers,
+2. Release managers are encouraged, but not required, to get approval from a
+   maintainer before approving patches. If there are no reachable maintainers,
    then release managers can ask approval from patch reviewers or other
    developers active in that area.
-
-#. *Before RC1* Patches should be limited to bug fixes, important optimization
+3. *Before RC1* Patches should be limited to bug fixes, important optimization
    improvements, or completion of features that were started before the branch
-   was created.  As with all phases, release managers and maintainers can reject
+   was created. As with all phases, release managers and maintainers can reject
    patches that are deemed too invasive.
-
-#. *Before RC2/RC3* Patches should be limited to bug fixes or backend-specific
+4. *Before RC2/RC3* Patches should be limited to bug fixes or backend-specific
    improvements that are determined to be very safe.
-
-#. *Before Final Major Release* Patches should be limited to critical
+5. *Before Final Major Release* Patches should be limited to critical
    bugs or regressions.
-
-#. *Bug fix releases* Patches should be limited to bug fixes or very safe
-   and critical performance improvements.  Patches must maintain both API and
+6. *Bug fix releases* Patches should be limited to bug fixes or very safe
+   and critical performance improvements. Patches must maintain both API and
    ABI compatibility with the X.1.0 release.
 
-Release Final Tasks
--------------------
+### Release Final Tasks
 
 The final stages of the release process involve tagging the "final" release
 branch, updating documentation that refers to the release, and updating the
 demo page.
 
-Update Documentation
-^^^^^^^^^^^^^^^^^^^^
+#### Update Documentation
 
 Review the documentation in the release branch and ensure that it is up
-to date.  The "Release Notes" must be updated to reflect new features, bug
+to date. The "Release Notes" must be updated to reflect new features, bug
 fixes, new known issues, and changes in the list of supported platforms.
-The :doc:`GettingStarted` page should be updated to reflect the new release
+The {doc}`GettingStarted` page should be updated to reflect the new release
 version number tag and changes in basic system requirements.
 
-.. _tag:
+(tag)=
 
-Tag the LLVM Final Release
-^^^^^^^^^^^^^^^^^^^^^^^^^^
+#### Tag the LLVM Final Release
 
 Tag the final release sources:
 
-::
-
-  $ git tag -sa llvmorg-X.Y.Z
-  $ git push https://github.com/llvm/llvm-project.git llvmorg-X.Y.Z
+```
+$ git tag -sa llvmorg-X.Y.Z
+$ git push https://github.com/llvm/llvm-project.git llvmorg-X.Y.Z
+```
 
-Update the LLVM Website
-^^^^^^^^^^^^^^^^^^^^^^^
+#### Update the LLVM Website
 
-The website must be updated before the release announcement is sent out.  Here
+The website must be updated before the release announcement is sent out. Here
 is what to do:
 
-#. Check out the `www-releases <https://github.com/llvm/www-releases>`_ repo
+1. Check out the [www-releases](https://github.com/llvm/www-releases) repo
    from GitHub.
-
-#. Create a new sub-directory ``X.Y.Z`` in the releases directory.
-
-#. Copy and commit the ``llvm/docs`` and ``LICENSE.txt`` files into this new
+2. Create a new sub-directory `X.Y.Z` in the releases directory.
+3. Copy and commit the `llvm/docs` and `LICENSE.txt` files into this new
    directory.
-
-#. Update the ``releases/download.html`` file with links to the release
+4. Update the `releases/download.html` file with links to the release
    binaries on GitHub.
-
-#. Update the ``releases/index.html`` with the new release and link to release
+5. Update the `releases/index.html` with the new release and link to release
    documentation.
-
-#. After you push the changes to the ``www-releases`` repo, someone with admin
-   access must log in to ``prereleases-origin.llvm.org`` and manually pull the new
-   changes into ``/data/www-releases/``. This is where the website is served from.
-
-#. Finally, check out the ``llvm-www`` repo and update the main page
-   (``index.html`` and sidebar) to point to the new release and release
+6. After you push the changes to the `www-releases` repo, someone with admin
+   access must log in to `prereleases-origin.llvm.org` and manually pull the new
+   changes into `/data/www-releases/`. This is where the website is served from.
+7. Finally, check out the `llvm-www` repo and update the main page
+   (`index.html` and sidebar) to point to the new release and release
    announcement.
 
-Announce the Release
-^^^^^^^^^^^^^^^^^^^^
+#### Announce the Release
 
-Create a new post in the `Announce Category <https://discourse.llvm.org/c/announce>`_
-once all the release tasks are complete.  For X.1.0 releases, make sure to include a
-link to the release notes in the post.  For X.1.1+ releases, generate a changelog
+Create a new post in the [Announce Category](https://discourse.llvm.org/c/announce)
+once all the release tasks are complete. For X.1.0 releases, make sure to include a
+link to the release notes in the post. For X.1.1+ releases, generate a changelog
 using this command and add it to the post.
 
-::
-
-  $ git log --format="- %aN: [%s (%h)](https://github.com/llvm/llvm-project/commit/%H)" llvmorg-X.1.N-1..llvmorg-X.1.N
+```
+$ git log --format="- %aN: [%s (%h)](https://github.com/llvm/llvm-project/commit/%H)" llvmorg-X.1.N-1..llvmorg-X.1.N
+```
 
 Once the release has been announced, add a link to the announcement on the llvm
-homepage (from the ``llvm-www`` repo) in the "Release Emails" section.
+homepage (from the `llvm-www` repo) in the "Release Emails" section.
+
diff --git a/llvm/docs/HowToSetUpLLVMStyleRTTI.md b/llvm/docs/HowToSetUpLLVMStyleRTTI.md
index 1ddcb163a6105..f56cd1a88614b 100644
--- a/llvm/docs/HowToSetUpLLVMStyleRTTI.md
+++ b/llvm/docs/HowToSetUpLLVMStyleRTTI.md
@@ -1,474 +1,459 @@
-======================================================
-How to set up LLVM-style RTTI for your class hierarchy
-======================================================
+# How to set up LLVM-style RTTI for your class hierarchy
 
+## Background
 
-Background
-==========
-
-LLVM avoids using C++'s built in RTTI. Instead, it  pervasively uses its
+LLVM avoids using C++'s built in RTTI. Instead, it pervasively uses its
 own hand-rolled form of RTTI which is much more efficient and flexible,
 although it requires a bit more work from you as a class author.
 
 A description of how to use LLVM-style RTTI from a client's perspective is
-given in the `Programmer's Manual <ProgrammersManual.html#isa>`_. This
+given in the [Programmer's Manual](ProgrammersManual.html#isa). This
 document, in contrast, discusses the steps you need to take as a class
 hierarchy author to make LLVM-style RTTI available to your clients.
 
 Before diving in, make sure that you are familiar with the Object Oriented
-Programming concept of "`is-a`_".
-
-.. _is-a: http://en.wikipedia.org/wiki/Is-a
+Programming concept of "[is-a][is-a]".
 
-Basic Setup
-===========
+## Basic Setup
 
 This section describes how to set up the most basic form of LLVM-style RTTI
 (which is sufficient for 99.9% of the cases). We will set up LLVM-style
 RTTI for this class hierarchy:
 
-.. code-block:: c++
-
-   class Shape {
-   public:
-     Shape() {}
-     virtual double computeArea() = 0;
-   };
-
-   class Square : public Shape {
-     double SideLength;
-   public:
-     Square(double S) : SideLength(S) {}
-     double computeArea() override;
-   };
-
-   class Circle : public Shape {
-     double Radius;
-   public:
-     Circle(double R) : Radius(R) {}
-     double computeArea() override;
-   };
+```c++
+class Shape {
+public:
+  Shape() {}
+  virtual double computeArea() = 0;
+};
+
+class Square : public Shape {
+  double SideLength;
+public:
+  Square(double S) : SideLength(S) {}
+  double computeArea() override;
+};
+
+class Circle : public Shape {
+  double Radius;
+public:
+  Circle(double R) : Radius(R) {}
+  double computeArea() override;
+};
+```
 
 The most basic working setup for LLVM-style RTTI requires the following
 steps:
 
-#. In the header where you declare ``Shape``, you will want to ``#include
-   "llvm/Support/Casting.h"``, which declares LLVM's RTTI templates. That
+1. In the header where you declare `Shape`, you will want to `#include
+   "llvm/Support/Casting.h"`, which declares LLVM's RTTI templates. That
    way your clients don't even have to think about it.
 
-   .. code-block:: c++
+   ```c++
+   #include "llvm/Support/Casting.h"
+   ```
 
-      #include "llvm/Support/Casting.h"
-
-#. In the base class, introduce an enum which discriminates all of the
+2. In the base class, introduce an enum which discriminates all of the
    different concrete classes in the hierarchy, and stash the enum value
    somewhere in the base class.
 
    Here is the code after introducing this change:
 
-   .. code-block:: c++
-
-       class Shape {
-       public:
-      +  /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
-      +  enum ShapeKind {
-      +    SK_Square,
-      +    SK_Circle
-      +  };
-      +private:
-      +  const ShapeKind Kind;
-      +public:
-      +  ShapeKind getKind() const { return Kind; }
-      +
-         Shape() {}
-         virtual double computeArea() = 0;
-       };
-
-   You will usually want to keep the ``Kind`` member encapsulated and
-   private, but let the enum ``ShapeKind`` be public along with providing a
-   ``getKind()`` method. This is convenient for clients so that they can do
-   a ``switch`` over the enum.
+   ```c++
+    class Shape {
+    public:
+   +  /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
+   +  enum ShapeKind {
+   +    SK_Square,
+   +    SK_Circle
+   +  };
+   +private:
+   +  const ShapeKind Kind;
+   +public:
+   +  ShapeKind getKind() const { return Kind; }
+   +
+      Shape() {}
+      virtual double computeArea() = 0;
+    };
+   ```
+
+   You will usually want to keep the `Kind` member encapsulated and
+   private, but let the enum `ShapeKind` be public along with providing a
+   `getKind()` method. This is convenient for clients so that they can do
+   a `switch` over the enum.
 
    A common naming convention is that these enums are "kind"s, to avoid
    ambiguity with the words "type" or "class" which have overloaded meanings
    in many contexts within LLVM. Sometimes there will be a natural name for
-   it, like "opcode". Don't bikeshed over this; when in doubt use ``Kind``.
+   it, like "opcode". Don't bikeshed over this; when in doubt use `Kind`.
 
-   You might wonder why the ``Kind`` enum doesn't have an entry for
-   ``Shape``. The reason for this is that since ``Shape`` is abstract
-   (``computeArea() = 0;``), you will never actually have non-derived
-   instances of exactly that class (only subclasses). See `Concrete Bases
-   and Deeper Hierarchies`_ for information on how to deal with
+   You might wonder why the `Kind` enum doesn't have an entry for
+   `Shape`. The reason for this is that since `Shape` is abstract
+   (`computeArea() = 0;`), you will never actually have non-derived
+   instances of exactly that class (only subclasses). See [Concrete Bases
+   and Deeper Hierarchies][concrete bases and deeper hierarchies] for information on how to deal with
    non-abstract bases. It's worth mentioning here that unlike
-   ``dynamic_cast<>``, LLVM-style RTTI can be used (and is often used) for
+   `dynamic_cast<>`, LLVM-style RTTI can be used (and is often used) for
    classes that don't have v-tables.
 
-#. Next, you need to make sure that the ``Kind`` gets initialized to the
+3. Next, you need to make sure that the `Kind` gets initialized to the
    value corresponding to the dynamic type of the class. Typically, you will
    want to have it be an argument to the constructor of the base class, and
-   then pass in the respective ``XXXKind`` from subclass constructors.
+   then pass in the respective `XXXKind` from subclass constructors.
 
    Here is the code after that change:
 
-   .. code-block:: c++
-
-       class Shape {
-       public:
-         /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
-         enum ShapeKind {
-           SK_Square,
-           SK_Circle
-         };
-       private:
-         const ShapeKind Kind;
-       public:
-         ShapeKind getKind() const { return Kind; }
-
-      -  Shape() {}
-      +  Shape(ShapeKind K) : Kind(K) {}
-         virtual double computeArea() = 0;
-       };
-
-       class Square : public Shape {
-         double SideLength;
-       public:
-      -  Square(double S) : SideLength(S) {}
-      +  Square(double S) : Shape(SK_Square), SideLength(S) {}
-         double computeArea() override;
-       };
-
-       class Circle : public Shape {
-         double Radius;
-       public:
-      -  Circle(double R) : Radius(R) {}
-      +  Circle(double R) : Shape(SK_Circle), Radius(R) {}
-         double computeArea() override;
-       };
-
-#. Finally, you need to inform LLVM's RTTI templates how to dynamically
-   determine the type of a class (i.e. whether the ``isa<>``/``dyn_cast<>``
+   ```c++
+    class Shape {
+    public:
+      /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
+      enum ShapeKind {
+        SK_Square,
+        SK_Circle
+      };
+    private:
+      const ShapeKind Kind;
+    public:
+      ShapeKind getKind() const { return Kind; }
+
+   -  Shape() {}
+   +  Shape(ShapeKind K) : Kind(K) {}
+      virtual double computeArea() = 0;
+    };
+
+    class Square : public Shape {
+      double SideLength;
+    public:
+   -  Square(double S) : SideLength(S) {}
+   +  Square(double S) : Shape(SK_Square), SideLength(S) {}
+      double computeArea() override;
+    };
+
+    class Circle : public Shape {
+      double Radius;
+    public:
+   -  Circle(double R) : Radius(R) {}
+   +  Circle(double R) : Shape(SK_Circle), Radius(R) {}
+      double computeArea() override;
+    };
+   ```
+
+4. Finally, you need to inform LLVM's RTTI templates how to dynamically
+   determine the type of a class (i.e. whether the `isa<>`/`dyn_cast<>`
    should succeed). The default "99.9% of use cases" way to accomplish this
-   is through a small static member function ``classof``. In order to have
+   is through a small static member function `classof`. In order to have
    proper context for an explanation, we will display this code first, and
    then below describe each part:
 
-   .. code-block:: c++
-
-       class Shape {
-       public:
-         /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
-         enum ShapeKind {
-           SK_Square,
-           SK_Circle
-         };
-       private:
-         const ShapeKind Kind;
-       public:
-         ShapeKind getKind() const { return Kind; }
-
-         Shape(ShapeKind K) : Kind(K) {}
-         virtual double computeArea() = 0;
-       };
-
-       class Square : public Shape {
-         double SideLength;
-       public:
-         Square(double S) : Shape(SK_Square), SideLength(S) {}
-         double computeArea() override;
-      +
-      +  static bool classof(const Shape *S) {
-      +    return S->getKind() == SK_Square;
-      +  }
-       };
-
-       class Circle : public Shape {
-         double Radius;
-       public:
-         Circle(double R) : Shape(SK_Circle), Radius(R) {}
-         double computeArea() override;
-      +
-      +  static bool classof(const Shape *S) {
-      +    return S->getKind() == SK_Circle;
-      +  }
-       };
-
-   The job of ``classof`` is to dynamically determine whether an object of
-   a base class is in fact of a particular derived class.  In order to
-   downcast a type ``Base`` to a type ``Derived``, there needs to be a
-   ``classof`` in ``Derived`` which will accept an object of type ``Base``.
+   ```c++
+    class Shape {
+    public:
+      /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
+      enum ShapeKind {
+        SK_Square,
+        SK_Circle
+      };
+    private:
+      const ShapeKind Kind;
+    public:
+      ShapeKind getKind() const { return Kind; }
 
-   To be concrete, consider the following code:
+      Shape(ShapeKind K) : Kind(K) {}
+      virtual double computeArea() = 0;
+    };
+
+    class Square : public Shape {
+      double SideLength;
+    public:
+      Square(double S) : Shape(SK_Square), SideLength(S) {}
+      double computeArea() override;
+   +
+   +  static bool classof(const Shape *S) {
+   +    return S->getKind() == SK_Square;
+   +  }
+    };
+
+    class Circle : public Shape {
+      double Radius;
+    public:
+      Circle(double R) : Shape(SK_Circle), Radius(R) {}
+      double computeArea() override;
+   +
+   +  static bool classof(const Shape *S) {
+   +    return S->getKind() == SK_Circle;
+   +  }
+    };
+   ```
 
-   .. code-block:: c++
+   The job of `classof` is to dynamically determine whether an object of
+   a base class is in fact of a particular derived class. In order to
+   downcast a type `Base` to a type `Derived`, there needs to be a
+   `classof` in `Derived` which will accept an object of type `Base`.
 
-      Shape *S = ...;
-      if (isa<Circle>(S)) {
-        /* do something ... */
-      }
+   To be concrete, consider the following code:
+
+   ```c++
+   Shape *S = ...;
+   if (isa<Circle>(S)) {
+     /* do something ... */
+   }
+   ```
 
-   The code of the ``isa<>`` test in this code will eventually boil
+   The code of the `isa<>` test in this code will eventually boil
    down---after template instantiation and some other machinery---to a
-   check roughly like ``Circle::classof(S)``. For more information, see
-   :ref:`classof-contract`.
+   check roughly like `Circle::classof(S)`. For more information, see
+   {ref}`classof-contract`.
 
-   The argument to ``classof`` should always be an *ancestor* class because
+   The argument to `classof` should always be an *ancestor* class because
    the implementation has logic to allow and optimize away
-   upcasts/up-``isa<>``'s automatically. It is as though every class
-   ``Foo`` automatically has a ``classof`` like:
-
-   .. code-block:: c++
-
-      class Foo {
-        [...]
-        template <class T>
-        static bool classof(const T *,
-                            ::std::enable_if<
-                              ::std::is_base_of<Foo, T>::value
-                            >::type* = 0) { return true; }
-        [...]
-      };
+   upcasts/up-`isa<>`'s automatically. It is as though every class
+   `Foo` automatically has a `classof` like:
+
+   ```c++
+   class Foo {
+     [...]
+     template <class T>
+     static bool classof(const T *,
+                         ::std::enable_if<
+                           ::std::is_base_of<Foo, T>::value
+                         >::type* = 0) { return true; }
+     [...]
+   };
+   ```
 
    Note that this is the reason that we did not need to introduce a
-   ``classof`` into ``Shape``: all relevant classes derive from ``Shape``,
-   and ``Shape`` itself is abstract (has no entry in the ``Kind`` enum),
-   so this notional inferred ``classof`` is all we need. See `Concrete
-   Bases and Deeper Hierarchies`_ for more information about how to extend
+   `classof` into `Shape`: all relevant classes derive from `Shape`,
+   and `Shape` itself is abstract (has no entry in the `Kind` enum),
+   so this notional inferred `classof` is all we need. See [Concrete
+   Bases and Deeper Hierarchies][concrete bases and deeper hierarchies] for more information about how to extend
    this example to more general hierarchies.
 
 Although for this small example setting up LLVM-style RTTI seems like a lot
 of "boilerplate", if your classes are doing anything interesting then this
 will end up being a tiny fraction of the code.
 
-Concrete Bases and Deeper Hierarchies
-=====================================
+## Concrete Bases and Deeper Hierarchies
 
 For concrete bases (i.e. non-abstract interior nodes of the inheritance
-tree), the ``Kind`` check inside ``classof`` needs to be a bit more
+tree), the `Kind` check inside `classof` needs to be a bit more
 complicated. The situation differs from the example above in that
 
-* Since the class is concrete, it must itself have an entry in the ``Kind``
+- Since the class is concrete, it must itself have an entry in the `Kind`
   enum because it is possible to have objects with this class as a dynamic
   type.
-
-* Since the class has children, the check inside ``classof`` must take them
+- Since the class has children, the check inside `classof` must take them
   into account.
 
-Say that ``SpecialSquare`` and ``OtherSpecialSquare`` derive
-from ``Square``, and so ``ShapeKind`` becomes:
-
-.. code-block:: c++
-
-    enum ShapeKind {
-      SK_Square,
-   +  SK_SpecialSquare,
-   +  SK_OtherSpecialSquare,
-      SK_Circle
-    }
-
-Then in ``Square``, we would need to modify the ``classof`` like so:
-
-.. code-block:: c++
-
-   -  static bool classof(const Shape *S) {
-   -    return S->getKind() == SK_Square;
-   -  }
-   +  static bool classof(const Shape *S) {
-   +    return S->getKind() >= SK_Square &&
-   +           S->getKind() <= SK_OtherSpecialSquare;
-   +  }
+Say that `SpecialSquare` and `OtherSpecialSquare` derive
+from `Square`, and so `ShapeKind` becomes:
+
+```c++
+ enum ShapeKind {
+   SK_Square,
++  SK_SpecialSquare,
++  SK_OtherSpecialSquare,
+   SK_Circle
+ }
+```
+
+Then in `Square`, we would need to modify the `classof` like so:
+
+```c++
+-  static bool classof(const Shape *S) {
+-    return S->getKind() == SK_Square;
+-  }
++  static bool classof(const Shape *S) {
++    return S->getKind() >= SK_Square &&
++           S->getKind() <= SK_OtherSpecialSquare;
++  }
+```
 
 The reason that we need to test a range like this instead of just equality
-is that both ``SpecialSquare`` and ``OtherSpecialSquare`` "is-a"
-``Square``, and so ``classof`` needs to return ``true`` for them.
+is that both `SpecialSquare` and `OtherSpecialSquare` "is-a"
+`Square`, and so `classof` needs to return `true` for them.
 
 This approach can be made to scale to arbitrarily deep hierarchies. The
 trick is that you arrange the enum values so that they correspond to a
 preorder traversal of the class hierarchy tree. With that arrangement, all
 subclass tests can be done with two comparisons as shown above. If you just
 list the class hierarchy like a list of bullet points, you'll get the
-ordering right::
+ordering right:
 
-   | Shape
-     | Square
-       | SpecialSquare
-       | OtherSpecialSquare
-     | Circle
+```
+| Shape
+  | Square
+    | SpecialSquare
+    | OtherSpecialSquare
+  | Circle
+```
 
-A Bug to be Aware Of
---------------------
+### A Bug to be Aware Of
 
-The example just given opens the door to bugs where the ``classof``\s are
-not updated to match the ``Kind`` enum when adding (or removing) classes to
+The example just given opens the door to bugs where the `classof`s are
+not updated to match the `Kind` enum when adding (or removing) classes to
 (from) the hierarchy.
 
-Continuing the example above, suppose we add a ``SomewhatSpecialSquare`` as
-a subclass of ``Square``, and update the ``ShapeKind`` enum like so:
+Continuing the example above, suppose we add a `SomewhatSpecialSquare` as
+a subclass of `Square`, and update the `ShapeKind` enum like so:
 
-.. code-block:: c++
+```c++
+ enum ShapeKind {
+   SK_Square,
+   SK_SpecialSquare,
+   SK_OtherSpecialSquare,
++  SK_SomewhatSpecialSquare,
+   SK_Circle
+ }
+```
 
-    enum ShapeKind {
-      SK_Square,
-      SK_SpecialSquare,
-      SK_OtherSpecialSquare,
-   +  SK_SomewhatSpecialSquare,
-      SK_Circle
-    }
-
-Now, suppose that we forget to update ``Square::classof()``, so it still
+Now, suppose that we forget to update `Square::classof()`, so it still
 looks like:
 
-.. code-block:: c++
-
-   static bool classof(const Shape *S) {
-     // BUG: Returns false when S->getKind() == SK_SomewhatSpecialSquare,
-     // even though SomewhatSpecialSquare "is a" Square.
-     return S->getKind() >= SK_Square &&
-            S->getKind() <= SK_OtherSpecialSquare;
-   }
+```c++
+static bool classof(const Shape *S) {
+  // BUG: Returns false when S->getKind() == SK_SomewhatSpecialSquare,
+  // even though SomewhatSpecialSquare "is a" Square.
+  return S->getKind() >= SK_Square &&
+         S->getKind() <= SK_OtherSpecialSquare;
+}
+```
 
 As the comment indicates, this code contains a bug. A straightforward and
-non-clever way to avoid this is to introduce an explicit ``SK_LastSquare``
+non-clever way to avoid this is to introduce an explicit `SK_LastSquare`
 entry in the enum when adding the first subclass(es). For example, we could
-rewrite the example at the beginning of `Concrete Bases and Deeper
-Hierarchies`_ as:
-
-.. code-block:: c++
-
-    enum ShapeKind {
-      SK_Square,
-   +  SK_SpecialSquare,
-   +  SK_OtherSpecialSquare,
-   +  SK_LastSquare,
-      SK_Circle
-    }
-   ...
-   // Square::classof()
-   -  static bool classof(const Shape *S) {
-   -    return S->getKind() == SK_Square;
-   -  }
-   +  static bool classof(const Shape *S) {
-   +    return S->getKind() >= SK_Square &&
-   +           S->getKind() <= SK_LastSquare;
-   +  }
+rewrite the example at the beginning of [Concrete Bases and Deeper
+Hierarchies][concrete bases and deeper hierarchies] as:
+
+```c++
+ enum ShapeKind {
+   SK_Square,
++  SK_SpecialSquare,
++  SK_OtherSpecialSquare,
++  SK_LastSquare,
+   SK_Circle
+ }
+...
+// Square::classof()
+-  static bool classof(const Shape *S) {
+-    return S->getKind() == SK_Square;
+-  }
++  static bool classof(const Shape *S) {
++    return S->getKind() >= SK_Square &&
++           S->getKind() <= SK_LastSquare;
++  }
+```
 
 Then, adding new subclasses is easy:
 
-.. code-block:: c++
+```c++
+ enum ShapeKind {
+   SK_Square,
+   SK_SpecialSquare,
+   SK_OtherSpecialSquare,
++  SK_SomewhatSpecialSquare,
+   SK_LastSquare,
+   SK_Circle
+ }
+```
 
-    enum ShapeKind {
-      SK_Square,
-      SK_SpecialSquare,
-      SK_OtherSpecialSquare,
-   +  SK_SomewhatSpecialSquare,
-      SK_LastSquare,
-      SK_Circle
-    }
+Notice that `Square::classof` does not need to be changed.
 
-Notice that ``Square::classof`` does not need to be changed.
+(classof-contract)=
 
-.. _classof-contract:
+### The Contract of `classof`
 
-The Contract of ``classof``
----------------------------
-
-To be more precise, let ``classof`` be inside a class ``C``.  Then the
-contract for ``classof`` is "return ``true`` if the dynamic type of the
-argument is-a ``C``".  As long as your implementation fulfills this
+To be more precise, let `classof` be inside a class `C`. Then the
+contract for `classof` is "return `true` if the dynamic type of the
+argument is-a `C`". As long as your implementation fulfills this
 contract, you can tweak and optimize it as much as you want.
 
 For example, LLVM-style RTTI can work fine in the presence of
-multiple-inheritance by defining an appropriate ``classof``.
+multiple-inheritance by defining an appropriate `classof`.
 An example of this in practice is
-`Decl <https://clang.llvm.org/doxygen/classclang_1_1Decl.html>`_ vs.
-`DeclContext <https://clang.llvm.org/doxygen/classclang_1_1DeclContext.html>`_
+[Decl](https://clang.llvm.org/doxygen/classclang_1_1Decl.html) vs.
+[DeclContext](https://clang.llvm.org/doxygen/classclang_1_1DeclContext.html)
 inside Clang.
-The ``Decl`` hierarchy is done very similarly to the example setup
+The `Decl` hierarchy is done very similarly to the example setup
 demonstrated in this tutorial.
-The key part is how to then incorporate ``DeclContext``: all that is needed
-is in ``bool DeclContext::classof(const Decl *)``, which asks the question
-"Given a ``Decl``, how can I determine if it is-a ``DeclContext``?".
-It answers this with a simple switch over the set of ``Decl`` "kinds", and
-returning true for ones that are known to be ``DeclContext``'s.
+The key part is how to then incorporate `DeclContext`: all that is needed
+is in `bool DeclContext::classof(const Decl *)`, which asks the question
+"Given a `Decl`, how can I determine if it is-a `DeclContext`?".
+It answers this with a simple switch over the set of `Decl` "kinds", and
+returning true for ones that are known to be `DeclContext`'s.
 
-Rules of Thumb
-==============
+## Rules of Thumb
 
-#. The ``Kind`` enum should have one entry per concrete class, ordered
+1. The `Kind` enum should have one entry per concrete class, ordered
    according to a preorder traversal of the inheritance tree.
-#. The argument to ``classof`` should be a ``const Base *``, where ``Base``
+2. The argument to `classof` should be a `const Base *`, where `Base`
    is some ancestor in the inheritance hierarchy. The argument should
    *never* be a derived class or the class itself: the template machinery
-   for ``isa<>`` already handles this case and optimizes it.
-#. For each class in the hierarchy that has no children, implement a
-   ``classof`` that checks only against its ``Kind``.
-#. For each class in the hierarchy that has children, implement a
-   ``classof`` that checks a range of the first child's ``Kind`` and the
-   last child's ``Kind``.
+   for `isa<>` already handles this case and optimizes it.
+3. For each class in the hierarchy that has no children, implement a
+   `classof` that checks only against its `Kind`.
+4. For each class in the hierarchy that has children, implement a
+   `classof` that checks a range of the first child's `Kind` and the
+   last child's `Kind`.
 
-RTTI for Open Class Hierarchies
-===============================
+## RTTI for Open Class Hierarchies
 
 Sometimes it is not possible to know all types in a hierarchy ahead of time.
 For example, in the shapes hierarchy described above the authors may have
 wanted their code to work for user defined shapes too. To support use cases
-that require open hierarchies LLVM provides the ``RTTIRoot`` and
-``RTTIExtends`` utilities.
+that require open hierarchies LLVM provides the `RTTIRoot` and
+`RTTIExtends` utilities.
 
-The ``RTTIRoot`` class describes an interface for performing RTTI checks. The
-``RTTIExtends`` class template provides an implementation of this interface
-for classes derived from ``RTTIRoot``. ``RTTIExtends`` uses the "`Curiously
-Recurring Template Idiom`_", taking the class being defined as its first
+The `RTTIRoot` class describes an interface for performing RTTI checks. The
+`RTTIExtends` class template provides an implementation of this interface
+for classes derived from `RTTIRoot`. `RTTIExtends` uses the "[Curiously
+Recurring Template Idiom][curiously recurring template idiom]", taking the class being defined as its first
 template argument and the parent class as the second argument. Any class that
-uses ``RTTIExtends`` must define a ``static char ID`` member, the address of
+uses `RTTIExtends` must define a `static char ID` member, the address of
 which will be used to identify the type.
 
 This open-hierarchy RTTI support should only be used if your use case requires
 it. Otherwise the standard LLVM RTTI system should be preferred.
 
-.. _`Curiously Recurring Template Idiom`:
-  https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
-
 E.g.
 
-.. code-block:: c++
+```c++
+class Shape : public RTTIExtends<Shape, RTTIRoot> {
+public:
+  static char ID;
+  virtual double computeArea() = 0;
+};
 
-   class Shape : public RTTIExtends<Shape, RTTIRoot> {
-   public:
-     static char ID;
-     virtual double computeArea() = 0;
-   };
+class Square : public RTTIExtends<Square, Shape> {
+  double SideLength;
+public:
+  static char ID;
 
-   class Square : public RTTIExtends<Square, Shape> {
-     double SideLength;
-   public:
-     static char ID;
-
-     Square(double S) : SideLength(S) {}
-     double computeArea() override;
-   };
+  Square(double S) : SideLength(S) {}
+  double computeArea() override;
+};
 
-   class Circle : public RTTIExtends<Circle, Shape> {
-     double Radius;
-   public:
-     static char ID;
+class Circle : public RTTIExtends<Circle, Shape> {
+  double Radius;
+public:
+  static char ID;
 
-     Circle(double R) : Radius(R) {}
-     double computeArea() override;
-   };
+  Circle(double R) : Radius(R) {}
+  double computeArea() override;
+};
 
-   char Shape::ID = 0;
-   char Square::ID = 0;
-   char Circle::ID = 0;
+char Shape::ID = 0;
+char Square::ID = 0;
+char Circle::ID = 0;
+```
 
-Advanced Use Cases
-==================
+## Advanced Use Cases
 
 The underlying implementation of isa/cast/dyn_cast is all controlled through a
-struct called ``CastInfo``. ``CastInfo`` provides 4 methods, ``isPossible``,
-``doCast``, ``castFailed``, and ``doCastIfPossible``. These are for ``isa``,
-``cast``, and ``dyn_cast``, in order. You can control the way your cast is
-performed by creating a specialization of the ``CastInfo`` struct (to your
-desired types) that provides the same static methods as the base ``CastInfo``
+struct called `CastInfo`. `CastInfo` provides 4 methods, `isPossible`,
+`doCast`, `castFailed`, and `doCastIfPossible`. These are for `isa`,
+`cast`, and `dyn_cast`, in order. You can control the way your cast is
+performed by creating a specialization of the `CastInfo` struct (to your
+desired types) that provides the same static methods as the base `CastInfo`
 struct.
 
 This can be a lot of boilerplate, so we also have what we call Cast Traits.
@@ -479,125 +464,128 @@ usage. These examples are not exhaustive, and adding new cast traits is easy
 so users should feel free to add them to their project, or contribute them if
 they're particularly useful!
 
-Enabling isa/cast/dyn_cast for Handle Types by Specializing ``simplify_type``
---------------------------------------------------------------------------------
+### Enabling isa/cast/dyn_cast for Handle Types by Specializing `simplify_type`
 
 It is common to require pointer handle types like smart pointers or iterators. Yet, since the
-``classof`` method is only implemented for the underlying types, developers must manually unwrap
-them into raw pointers prior to invoking ``isa``, ``cast``, or ``dyn_cast``.
-
-To avoid this boilerplate, you can specialize the ``simplify_type`` template for your handle type.
-For example, if you have an iterator class for ``Shape`` called ``ShapeIterator``, you can specialize
-``simplify_type`` like so:
-
-.. code-block:: c++
+`classof` method is only implemented for the underlying types, developers must manually unwrap
+them into raw pointers prior to invoking `isa`, `cast`, or `dyn_cast`.
+
+To avoid this boilerplate, you can specialize the `simplify_type` template for your handle type.
+For example, if you have an iterator class for `Shape` called `ShapeIterator`, you can specialize
+`simplify_type` like so:
+
+```c++
+class ShapeIterator {
+public:
+  ShapeIterator(Shape *ptr) : ptr(ptr) {}
+  Shape *get() const { return ptr; }
+private:
+  Shape *ptr;
+};
+
+template <>
+struct simplify_type<ShapeIterator> {
+  using SimpleType = Shape *;
+  static SimpleType getSimplifiedValue(const ShapeIterator &I) {
+    return I.get();
+  }
+};
+```
+
+By doing this, you can now use `isa`, `cast`, and `dyn_cast` directly on `ShapeIterator` objects
+without having to manually call `get()` on them. For example:
+
+```c++
+ShapeIterator it = ...;
+/* if (Square *S = dyn_cast<Square>(it.get())) */
+if (Square *S = dyn_cast<Square>(it)) {
+  /* do something with S ... */
+}
+```
+
+### Value to value casting
 
-    class ShapeIterator {
-    public:
-      ShapeIterator(Shape *ptr) : ptr(ptr) {}
-      Shape *get() const { return ptr; }
-    private:
-      Shape *ptr;
-    };
-
-    template <>
-    struct simplify_type<ShapeIterator> {
-      using SimpleType = Shape *;
-      static SimpleType getSimplifiedValue(const ShapeIterator &I) {
-        return I.get();
-      }
-    };
-
-By doing this, you can now use ``isa``, ``cast``, and ``dyn_cast`` directly on ``ShapeIterator`` objects
-without having to manually call ``get()`` on them. For example:
-
-.. code-block:: c++
-
-    ShapeIterator it = ...;
-    /* if (Square *S = dyn_cast<Square>(it.get())) */
-    if (Square *S = dyn_cast<Square>(it)) {
-      /* do something with S ... */
-    }
-
-Value to value casting
-----------------------
 In this case, we have a struct that is what we call 'nullable' - i.e. it is
-constructible from ``nullptr`` and that results in a value you can tell is
+constructible from `nullptr` and that results in a value you can tell is
 invalid.
 
-.. code-block:: c++
-
-  class SomeValue {
-  public:
-    SomeValue(void *ptr) : ptr(ptr) {}
-    void *getPointer() const { return ptr; }
-    bool isValid() const { return ptr != nullptr; }
-  private:
-    void *ptr;
-  };
+```c++
+class SomeValue {
+public:
+  SomeValue(void *ptr) : ptr(ptr) {}
+  void *getPointer() const { return ptr; }
+  bool isValid() const { return ptr != nullptr; }
+private:
+  void *ptr;
+};
+```
 
 Given something like this, we want to pass this object around by value, and we
 would like to cast from objects of this type to some other set of objects. For
-now, we assume that the types we want to cast *to* all provide ``classof``. So
+now, we assume that the types we want to cast *to* all provide `classof`. So
 we can use some provided cast traits like so:
 
-.. code-block:: c++
+```c++
+template <typename T>
+struct CastInfo<T, SomeValue>
+  : CastIsPossible<T, SomeValue>, NullableValueCastFailed<T>,
+    DefaultDoCastIfPossible<T, SomeValue, CastInfo<T, SomeValue>> {
+  static T doCast(SomeValue v) {
+    return T(v.getPointer());
+  }
+};
+```
 
-  template <typename T>
-  struct CastInfo<T, SomeValue>
-    : CastIsPossible<T, SomeValue>, NullableValueCastFailed<T>,
-      DefaultDoCastIfPossible<T, SomeValue, CastInfo<T, SomeValue>> {
-    static T doCast(SomeValue v) {
-      return T(v.getPointer());
-    }
-  };
+### Pointer to value casting
 
-Pointer to value casting
-------------------------
-Now given the value above ``SomeValue``, maybe we'd like to be able to cast to
+Now given the value above `SomeValue`, maybe we'd like to be able to cast to
 that type from a char pointer type. So what we would do in that case is:
 
-.. code-block:: c++
-
-  template <typename T>
-  struct CastInfo<SomeValue, T *>
-    : NullableValueCastFailed<SomeValue>,
-      DefaultDoCastIfPossible<SomeValue, T *, CastInfo<SomeValue, T *>> {
-    static bool isPossible(const T *t) {
-      return std::is_same<T, char>::value;
-    }
-    static SomeValue doCast(const T *t) {
-      return SomeValue((void *)t);
-    }
-  };
-
-This would enable us to cast from a ``char *`` to a SomeValue, if we wanted to.
-
-Optional value casting
-----------------------
-When your types are not constructible from ``nullptr`` or there isn't a simple
-way to tell when an object is invalid, you may want to use ``std::optional``.
+```c++
+template <typename T>
+struct CastInfo<SomeValue, T *>
+  : NullableValueCastFailed<SomeValue>,
+    DefaultDoCastIfPossible<SomeValue, T *, CastInfo<SomeValue, T *>> {
+  static bool isPossible(const T *t) {
+    return std::is_same<T, char>::value;
+  }
+  static SomeValue doCast(const T *t) {
+    return SomeValue((void *)t);
+  }
+};
+```
+
+This would enable us to cast from a `char *` to a SomeValue, if we wanted to.
+
+### Optional value casting
+
+When your types are not constructible from `nullptr` or there isn't a simple
+way to tell when an object is invalid, you may want to use `std::optional`.
 In those cases, you probably want something like this:
 
-.. code-block:: c++
-
-  template <typename T>
-  struct CastInfo<T, SomeValue> : OptionalValueCast<T, SomeValue> {};
+```c++
+template <typename T>
+struct CastInfo<T, SomeValue> : OptionalValueCast<T, SomeValue> {};
+```
 
-That cast trait requires that ``T`` is constructible from ``const SomeValue &``
+That cast trait requires that `T` is constructible from `const SomeValue &`
 but it enables casting like so:
 
-.. code-block:: c++
+```c++
+SomeValue someVal = ...;
+std::optional<AnotherValue> valOr = dyn_cast<AnotherValue>(someVal);
+```
 
-  SomeValue someVal = ...;
-  std::optional<AnotherValue> valOr = dyn_cast<AnotherValue>(someVal);
+With the `_if_present` variants, you can even do optional chaining like this:
 
-With the ``_if_present`` variants, you can even do optional chaining like this:
+```c++
+std::optional<SomeValue> someVal = ...;
+std::optional<AnotherValue> valOr = dyn_cast_if_present<AnotherValue>(someVal);
+```
 
-.. code-block:: c++
+and `valOr` will be `std::nullopt` if either `someVal` cannot be converted *or*
+if `someVal` was also `std::nullopt`.
 
-  std::optional<SomeValue> someVal = ...;
-  std::optional<AnotherValue> valOr = dyn_cast_if_present<AnotherValue>(someVal);
+[curiously recurring template idiom]: https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
+[is-a]: http://en.wikipedia.org/wiki/Is-a
 
-and ``valOr`` will be ``std::nullopt`` if either ``someVal`` cannot be converted *or*
-if ``someVal`` was also ``std::nullopt``.
diff --git a/llvm/docs/HowToSubmitABug.md b/llvm/docs/HowToSubmitABug.md
index e43bfa3e0341c..4c23b9f134f80 100644
--- a/llvm/docs/HowToSubmitABug.md
+++ b/llvm/docs/HowToSubmitABug.md
@@ -1,40 +1,34 @@
-================================
-How to submit an LLVM bug report
-================================
-
-Introduction - Got bugs?
-========================
+# How to submit an LLVM bug report
 
+## Introduction - Got bugs?
 
 If you're working with LLVM and encounter a bug, we definitely want to know
-about it.  This document describes what you can do to increase the odds of
+about it. This document describes what you can do to increase the odds of
 getting it fixed quickly.
 
-🔒 If you believe that the bug is security related, please follow :ref:`report-security-issue`. 🔒
+🔒 If you believe that the bug is security related, please follow {ref}`report-security-issue`. 🔒
 
 Basically, you have to do two things at a minimum. First, decide whether the
-bug `crashes the compiler`_ or if the compiler is `miscompiling`_ the program
+bug [crashes the compiler] or if the compiler is [miscompiling] the program
 (i.e., the compiler successfully produces an executable, but it doesn't run
 right). Based on what type of bug it is, follow the instructions in the
 linked section to narrow down the bug so that the person who fixes it will be
 able to find the problem more easily.
 
-Once you have a reduced test case, go to `the LLVM Bug Tracking System
-<https://github.com/llvm/llvm-project/issues>`_ and fill out the form with the
+Once you have a reduced test case, go to [the LLVM Bug Tracking System](https://github.com/llvm/llvm-project/issues) and fill out the form with the
 necessary details (note that you don't need to pick a label, just use if you're
-not sure).  The bug description should contain the following information:
+not sure). The bug description should contain the following information:
 
-* All information necessary to reproduce the problem.
-* The reduced test case that triggers the bug.
-* The location where you obtained LLVM (if not from our Git
+- All information necessary to reproduce the problem.
+- The reduced test case that triggers the bug.
+- The location where you obtained LLVM (if not from our Git
   repository).
 
 Thanks for helping us make LLVM better!
 
-.. _crashes the compiler:
+(crashes-the-compiler)=
 
-Crashing Bugs
-=============
+## Crashing Bugs
 
 More often than not, bugs in the compiler cause it to crash---often due to
 an assertion failure of some sort. The most important piece of the puzzle
@@ -43,197 +37,192 @@ the LLVM libraries (e.g., the optimizer or code generator) that has
 problems.
 
 To identify the crashing component (the front-end, middle-end
-optimizer, or backend code generator), run the ``clang`` command line as you
+optimizer, or backend code generator), run the `clang` command line as you
 were when the crash occurred, but with the following extra command line
 options:
 
-* ``-emit-llvm -Xclang -disable-llvm-passes``: If ``clang`` still crashes when
+- `-emit-llvm -Xclang -disable-llvm-passes`: If `clang` still crashes when
   passed these options (which disable the optimizer and code generator), then
-  the crash is in the front-end. Jump ahead to :ref:`front-end bugs
+  the crash is in the front-end. Jump ahead to {ref}`front-end bugs
   <frontend-crash>`.
-
-* ``-emit-llvm``: If ``clang`` crashes with this option (which disables
+- `-emit-llvm`: If `clang` crashes with this option (which disables
   the code generator), you've found a middle-end optimizer bug. Jump ahead to
-  :ref:`middle-end bugs <middleend-crash>`.
-
-* Otherwise, you have a backend code generator crash. Jump ahead to :ref:`code
+  {ref}`middle-end bugs <middleend-crash>`.
+- Otherwise, you have a backend code generator crash. Jump ahead to {ref}`code
   generator bugs <backend-crash>`.
 
-.. _frontend-crash:
+(frontend-crash)=
 
-Front-end bugs
---------------
+### Front-end bugs
 
-On a ``clang`` crash, the compiler will dump a preprocessed file and a script
-to replay the ``clang`` command. For example, you should see something like
+On a `clang` crash, the compiler will dump a preprocessed file and a script
+to replay the `clang` command. For example, you should see something like
 
-.. code-block:: text
+```text
+PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
+Preprocessed source(s) and associated run script(s) are located at:
+clang: note: diagnostic msg: /tmp/foo-xxxxxx.c
+clang: note: diagnostic msg: /tmp/foo-xxxxxx.sh
+```
 
-   PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
-   Preprocessed source(s) and associated run script(s) are located at:
-   clang: note: diagnostic msg: /tmp/foo-xxxxxx.c
-   clang: note: diagnostic msg: /tmp/foo-xxxxxx.sh
-
-The `creduce <https://github.com/csmith-project/creduce>`_ tool helps to
+The [creduce](https://github.com/csmith-project/creduce) tool helps to
 reduce the preprocessed file down to the smallest amount of code that still
 replicates the problem. You're encouraged to use creduce to reduce the code
 to make the developers' lives easier. The
-``clang/utils/reduce-clang-crash.py`` script can be used on the files
+`clang/utils/reduce-clang-crash.py` script can be used on the files
 that clang dumps to help with automating creating a test to check for the
 compiler crash.
 
-`cvise <https://github.com/marxin/cvise>`_ is an alternative to ``creduce``.
+[cvise](https://github.com/marxin/cvise) is an alternative to `creduce`.
 
-.. _middleend-crash:
+(middleend-crash)=
 
-Middle-end optimization bugs
-----------------------------
+### Middle-end optimization bugs
 
 If you find that a bug crashes in the optimizer, compile your test-case to a
-``.bc`` file by passing "``-emit-llvm -O1 -Xclang -disable-llvm-passes -c -o
-foo.bc``". The ``-O1`` is important because ``-O0`` adds the ``optnone``
-function attribute to all functions and many passes don't run on ``optnone``
+`.bc` file by passing "`-emit-llvm -O1 -Xclang -disable-llvm-passes -c -o
+foo.bc`". The `-O1` is important because `-O0` adds the `optnone`
+function attribute to all functions and many passes don't run on `optnone`
 functions. Then run:
 
-.. code-block:: bash
-
-   opt -O3 foo.bc -disable-output
+```bash
+opt -O3 foo.bc -disable-output
+```
 
-If this doesn't crash, please follow the instructions for a :ref:`front-end
+If this doesn't crash, please follow the instructions for a {ref}`front-end
 bug <frontend-crash>`.
 
-If this does crash, then you can debug this with the :doc:`llvm-reduce
+If this does crash, then you can debug this with the {doc}`llvm-reduce
 <CommandGuide/llvm-reduce>` tool. Create a script that reproduces the
 crash and run:
 
-.. code-block:: bash
-
-   llvm-reduce --test=path/to/script foo.bc
+```bash
+llvm-reduce --test=path/to/script foo.bc
+```
 
 which should produce reduced IR that reproduces the crash.
 
-.. TIP::
-   ``llvm-reduce -j $NUM_THREADS`` is multi-threaded and can therefore
-   potentially be much faster.
-
-.. TIP::
-   Reduction is fastest and most effective the simpler the
-   reproduction script is. Ideally, this will be running `opt` with a
-   single pass. The most effective way to extract the IR before a
-   specific point is a two step process. First, run the testcase with
-   the ``-print-pass-numbers`` flag. This will print the name of a
-   pass and an integer ID. You can then use the last ID printed before
-   the crash and add 3 flags,
-   ``-print-before-pass-number=<integer-id> -print-module-scope -ir-dump-directory=/my/debug/path``.
-   This will place failing IR files in the given directory.
-   ``-print-before-pass-number`` is the minimum required flag, but
-   will not produce an output directly consumable by a tool. It will
-   print to stderr, and will be incomplete in most situations without
-   ``-print-module-scope``.
-
-   A more brute force approach is to use the ``--print-before-all
-   --print-module-scope`` flags to dump the IR before every pass. Be
-   warned that this is very verbose.
-
-.. _backend-crash:
-
-Backend code generator bugs
----------------------------
+:::{TIP}
+`llvm-reduce -j $NUM_THREADS` is multi-threaded and can therefore
+potentially be much faster.
+:::
+
+:::{TIP}
+Reduction is fastest and most effective the simpler the
+reproduction script is. Ideally, this will be running `opt` with a
+single pass. The most effective way to extract the IR before a
+specific point is a two step process. First, run the testcase with
+the `-print-pass-numbers` flag. This will print the name of a
+pass and an integer ID. You can then use the last ID printed before
+the crash and add 3 flags,
+`-print-before-pass-number=<integer-id> -print-module-scope -ir-dump-directory=/my/debug/path`.
+This will place failing IR files in the given directory.
+`-print-before-pass-number` is the minimum required flag, but
+will not produce an output directly consumable by a tool. It will
+print to stderr, and will be incomplete in most situations without
+`-print-module-scope`.
+
+A more brute force approach is to use the `--print-before-all
+--print-module-scope` flags to dump the IR before every pass. Be
+warned that this is very verbose.
+:::
+
+(backend-crash)=
+
+### Backend code generator bugs
 
 If you find a bug that crashes clang in the code generator, compile your
-source file to a ``.bc`` file by passing "``-emit-llvm -c -o foo.bc``" to
-clang (in addition to the options you already pass).  Once you have
-``foo.bc``, one of the following commands should fail:
+source file to a `.bc` file by passing "`-emit-llvm -c -o foo.bc`" to
+clang (in addition to the options you already pass). Once you have
+`foo.bc`, one of the following commands should fail:
 
-#. ``llc foo.bc``
-#. ``llc foo.bc -relocation-model=pic``
-#. ``llc foo.bc -relocation-model=static``
+1. `llc foo.bc`
+2. `llc foo.bc -relocation-model=pic`
+3. `llc foo.bc -relocation-model=static`
 
 If none of these crash, please follow the instructions for a
-:ref:`front-end bug<frontend-crash>`. If one of these crashes, you
-should be able to reduce this with :doc:`llvm-reduce
+{ref}`front-end bug<frontend-crash>`. If one of these crashes, you
+should be able to reduce this with {doc}`llvm-reduce
 <CommandGuide/llvm-reduce>`, similar to middle end bugs. In this
-case, your test script should use :doc:`llc <CommandGuide/llc>`
+case, your test script should use {doc}`llc <CommandGuide/llc>`
 instead of `opt`.
 
 Please run this, then file a bug with the instructions and reduced
-``.bc`` file that `llvm-reduce` emits.  If something goes wrong with
-`llvm-reduce`, please submit the ``foo.bc`` file and the option that
+`.bc` file that `llvm-reduce` emits. If something goes wrong with
+`llvm-reduce`, please submit the `foo.bc` file and the option that
 `llc` crashes with.
 
-LTO bugs
----------------------------
+### LTO bugs
 
 If you encounter a bug that leads to crashes in the LLVM LTO phase when using
-the ``-flto`` option, follow these steps to diagnose and report the issue:
+the `-flto` option, follow these steps to diagnose and report the issue:
 
-Compile your source file to a ``.bc`` (Bitcode) file with the following options,
+Compile your source file to a `.bc` (Bitcode) file with the following options,
 in addition to your existing compilation options:
 
-.. code-block:: bash
-
-   export CFLAGS="-flto -fuse-ld=lld" CXXFLAGS="-flto -fuse-ld=lld" LDFLAGS="-Wl,-plugin-opt=save-temps"
+```bash
+export CFLAGS="-flto -fuse-ld=lld" CXXFLAGS="-flto -fuse-ld=lld" LDFLAGS="-Wl,-plugin-opt=save-temps"
+```
 
 These options enable LTO and save temporary files generated during compilation
 for later analysis.
 
-On Windows, use lld-link as the linker. Adjust your compilation 
+On Windows, use lld-link as the linker. Adjust your compilation
 flags as follows:
-* Add ``/lldsavetemps`` to the linker flags.
-* When linking from the compiler driver, add ``/link /lldsavetemps`` in order to forward that flag to the linker.
+\* Add `/lldsavetemps` to the linker flags.
+\* When linking from the compiler driver, add `/link /lldsavetemps` in order to forward that flag to the linker.
 
 Using the specified flags will generate four intermediate bytecode files:
 
-#. a.out.0.0.preopt.bc (Before any link-time optimizations (LTO) are applied)
-#. a.out.0.2.internalize.bc (After initial optimizations are applied)
-#. a.out.0.4.opt.bc (After an extensive set of optimizations)
-#. a.out.0.5.precodegen.bc (After LTO but before translating into machine code)
+1. a.out.0.0.preopt.bc (Before any link-time optimizations (LTO) are applied)
+2. a.out.0.2.internalize.bc (After initial optimizations are applied)
+3. a.out.0.4.opt.bc (After an extensive set of optimizations)
+4. a.out.0.5.precodegen.bc (After LTO but before translating into machine code)
 
 Execute one of the following commands to identify the source of the problem:
 
-#. ``opt "-passes=lto<O3>" a.out.0.2.internalize.bc``
-#. ``llc a.out.0.5.precodegen.bc``
+1. `opt "-passes=lto<O3>" a.out.0.2.internalize.bc`
+2. `llc a.out.0.5.precodegen.bc`
 
 If one of these do crash, you should be able to reduce
-this with :program:`llvm-reduce`
+this with {program}`llvm-reduce`
 command line (use the bc file corresponding to the command above that failed):
 
-.. code-block:: bash
-
-   llvm-reduce --test reduce.sh a.out.0.2.internalize.bc
+```bash
+llvm-reduce --test reduce.sh a.out.0.2.internalize.bc
+```
 
-Example of ``reduce.sh`` script
+Example of `reduce.sh` script
 
-.. code-block:: bash
+```bash
+$ cat reduce.sh
+#!/bin/bash -e
 
-   $ cat reduce.sh
-   #!/bin/bash -e
-
-   path/to/not --crash path/to/opt "-passes=lto<O3>" $1 -o temp.bc  2> err.log
-   grep -q "It->second == &Insn" err.log
+path/to/not --crash path/to/opt "-passes=lto<O3>" $1 -o temp.bc  2> err.log
+grep -q "It->second == &Insn" err.log
+```
 
 Here we have grepped for the failed assert message.
 
-Please run this, then file a bug with the instructions and reduced ``.bc`` file
+Please run this, then file a bug with the instructions and reduced `.bc` file
 that llvm-reduce emits.
 
-.. _miscompiling:
+(miscompiling)=
 
-Miscompilations
-===============
+## Miscompilations
 
 If clang successfully produces an executable, but that executable doesn't run
 right, this is either a bug in the code or a bug in the compiler. The first
 thing to check is to make sure it is not using undefined behavior (e.g.,
 reading a variable before it is defined). In particular, check to see if the
-program is clean under various `sanitizers
-<https://github.com/google/sanitizers>`_ (e.g., ``clang
--fsanitize=undefined,address``) and `valgrind <http://valgrind.org/>`_. Many
+program is clean under various [sanitizers](https://github.com/google/sanitizers) (e.g., `clang
+-fsanitize=undefined,address`) and [valgrind](http://valgrind.org/). Many
 "LLVM bugs" that we have chased down ended up being bugs in the program being
 compiled, not LLVM.
 
 Once you determine that the program itself is not buggy, you should work on
 reducing the inputs required to reproduce the miscompilation. The
-:doc:`OptBisect <OptBisect>` page shows how to find the optimization pass
-causing the miscompile. You can use :doc:`llvm-reduce <CommandGuide/llvm-reduce>`
+{doc}`OptBisect <OptBisect>` page shows how to find the optimization pass
+causing the miscompile. You can use {doc}`llvm-reduce <CommandGuide/llvm-reduce>`
 to minimize the bitcode necessary to reproduce the miscompilation.
+
diff --git a/llvm/docs/HowToUseInstrMappings.md b/llvm/docs/HowToUseInstrMappings.md
index 326c39fde93db..12bb9e64fdf45 100644
--- a/llvm/docs/HowToUseInstrMappings.md
+++ b/llvm/docs/HowToUseInstrMappings.md
@@ -1,10 +1,6 @@
-===============================
-How To Use Instruction Mappings
-===============================
+# How To Use Instruction Mappings
 
-
-Introduction
-============
+## Introduction
 
 This document contains information about adding instruction mapping support
 for a target. The motivation behind this feature comes from the need to switch
@@ -16,97 +12,95 @@ added in the .td files, all the relevant switch cases should be modified
 accordingly. Instead, the same functionality could be achieved with TableGen and
 some support from the .td files for a fraction of maintenance cost.
 
-``InstrMapping`` Class Overview
-===============================
+## `InstrMapping` Class Overview
 
 TableGen uses relationship models to map instructions with each other. These
-models are described using ``InstrMapping`` class as a base. Each model sets
-various fields of the ``InstrMapping`` class such that they can uniquely
+models are described using `InstrMapping` class as a base. Each model sets
+various fields of the `InstrMapping` class such that they can uniquely
 describe all the instructions using that model. TableGen parses all the relation
 models and uses the information to construct relation tables which relate
 instructions with each other. These tables are emitted in the
-``XXXInstrInfo.inc`` file along with the functions to query them. Following
-is the definition of ``InstrMapping`` class defined in Target.td file:
-
-.. code-block:: text
-
-  class InstrMapping {
-    // Used to reduce search space only to the instructions using this
-    // relation model.
-    string FilterClass;
-
-    // List of fields/attributes that should be same for all the instructions in
-    // a row of the relation table. Think of this as a set of properties shared
-    // by all the instructions related by this relationship.
-    list<string> RowFields = [];
-
-    // List of fields/attributes that are same for all the instructions
-    // in a column of the relation table.
-    list<string> ColFields = [];
-
-    // Values for the fields/attributes listed in 'ColFields' corresponding to
-    // the key instruction. This is the instruction that will be transformed
-    // using this relation model.
-    list<string> KeyCol = [];
-
-    // List of values for the fields/attributes listed in 'ColFields', one for
-    // each column in the relation table. These are the instructions a key
-    // instruction will be transformed into.
-    list<list<string> > ValueCols = [];
-  }
-
-Sample Example
---------------
+`XXXInstrInfo.inc` file along with the functions to query them. Following
+is the definition of `InstrMapping` class defined in Target.td file:
+
+```text
+class InstrMapping {
+  // Used to reduce search space only to the instructions using this
+  // relation model.
+  string FilterClass;
+
+  // List of fields/attributes that should be same for all the instructions in
+  // a row of the relation table. Think of this as a set of properties shared
+  // by all the instructions related by this relationship.
+  list<string> RowFields = [];
+
+  // List of fields/attributes that are same for all the instructions
+  // in a column of the relation table.
+  list<string> ColFields = [];
+
+  // Values for the fields/attributes listed in 'ColFields' corresponding to
+  // the key instruction. This is the instruction that will be transformed
+  // using this relation model.
+  list<string> KeyCol = [];
+
+  // List of values for the fields/attributes listed in 'ColFields', one for
+  // each column in the relation table. These are the instructions a key
+  // instruction will be transformed into.
+  list<list<string> > ValueCols = [];
+}
+```
+
+### Sample Example
 
 Let's say that we want to have a function
-``int getPredOpcode(uint32_t Opcode, enum PredSense inPredSense)`` which
+`int getPredOpcode(uint32_t Opcode, enum PredSense inPredSense)` which
 takes a non-predicated instruction and returns its predicated true or false form
-depending on some input flag, ``inPredSense``. The first step in the process is
+depending on some input flag, `inPredSense`. The first step in the process is
 to define a relationship model that relates predicated instructions to their
-non-predicated form by assigning appropriate values to the ``InstrMapping``
+non-predicated form by assigning appropriate values to the `InstrMapping`
 fields. For this relationship, non-predicated instructions are treated as key
 instruction since they are the ones used to query the interface function.
 
-.. code-block:: text
-
-  def getPredOpcode : InstrMapping {
-    // Choose a FilterClass that is used as a base class for all the
-    // instructions modeling this relationship. This is done to reduce the
-    // search space only to these set of instructions.
-    let FilterClass = "PredRel";
-
-    // Instructions with same values for all the fields in RowFields form a
-    // row in the resulting relation table.
-    // For example, if we want to relate 'ADD' (non-predicated) with 'Add_pt'
-    // (predicated true) and 'Add_pf' (predicated false), then all 3
-    // instructions need to have same value for BaseOpcode field. It can be any
-    // unique value (Ex: XYZ) and should not be shared with any other
-    // instruction not related to 'add'.
-    let RowFields = ["BaseOpcode"];
-
-    // List of attributes that can be used to define key and column instructions
-    // for a relation. Key instruction is passed as an argument
-    // to the function used for querying relation tables. Column instructions
-    // are the instructions they (key) can transform into.
-    //
-    // Here, we choose 'PredSense' as ColFields since this is the unique
-    // attribute of the key (non-predicated) and column (true/false)
-    // instructions involved in this relationship model.
-    let ColFields = ["PredSense"];
-
-    // The key column contains non-predicated instructions.
-    let KeyCol = ["none"];
-
-    // Two value columns - first column contains instructions with
-    // PredSense=true while second column has instructions with PredSense=false.
-    let ValueCols = [["true"], ["false"]];
-  }
+```text
+def getPredOpcode : InstrMapping {
+  // Choose a FilterClass that is used as a base class for all the
+  // instructions modeling this relationship. This is done to reduce the
+  // search space only to these set of instructions.
+  let FilterClass = "PredRel";
+
+  // Instructions with same values for all the fields in RowFields form a
+  // row in the resulting relation table.
+  // For example, if we want to relate 'ADD' (non-predicated) with 'Add_pt'
+  // (predicated true) and 'Add_pf' (predicated false), then all 3
+  // instructions need to have same value for BaseOpcode field. It can be any
+  // unique value (Ex: XYZ) and should not be shared with any other
+  // instruction not related to 'add'.
+  let RowFields = ["BaseOpcode"];
+
+  // List of attributes that can be used to define key and column instructions
+  // for a relation. Key instruction is passed as an argument
+  // to the function used for querying relation tables. Column instructions
+  // are the instructions they (key) can transform into.
+  //
+  // Here, we choose 'PredSense' as ColFields since this is the unique
+  // attribute of the key (non-predicated) and column (true/false)
+  // instructions involved in this relationship model.
+  let ColFields = ["PredSense"];
+
+  // The key column contains non-predicated instructions.
+  let KeyCol = ["none"];
+
+  // Two value columns - first column contains instructions with
+  // PredSense=true while second column has instructions with PredSense=false.
+  let ValueCols = [["true"], ["false"]];
+}
+```
 
 TableGen uses the above relationship model to emit relation table that maps
 non-predicated instructions with their predicated forms. It also outputs the
 interface function
-``int getPredOpcode(uint32_t Opcode, enum PredSense inPredSense)`` to query
-the table. Here, Function ``getPredOpcode`` takes two arguments, opcode of the
+`int getPredOpcode(uint32_t Opcode, enum PredSense inPredSense)` to query
+the table. Here, Function `getPredOpcode` takes two arguments, opcode of the
 current instruction and PredSense of the desired instruction, and returns
 predicated form of the instruction, if found in the relation table.
 In order for an instruction to be added into the relation table, it needs
@@ -114,60 +108,61 @@ to include relevant information in its definition. For example, consider
 following to be the current definitions of ADD, ADD_pt (true) and ADD_pf (false)
 instructions:
 
-.. code-block:: text
+```text
+def ADD : ALU32_rr<(outs IntRegs:$dst), (ins IntRegs:$a, IntRegs:$b),
+            "$dst = add($a, $b)",
+            [(set (i32 IntRegs:$dst), (add (i32 IntRegs:$a),
+                                           (i32 IntRegs:$b)))]>;
 
-  def ADD : ALU32_rr<(outs IntRegs:$dst), (ins IntRegs:$a, IntRegs:$b),
-              "$dst = add($a, $b)",
-              [(set (i32 IntRegs:$dst), (add (i32 IntRegs:$a),
-                                             (i32 IntRegs:$b)))]>;
+def ADD_Pt : ALU32_rr<(outs IntRegs:$dst),
+                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
+            "if ($p) $dst = add($a, $b)",
+            []>;
 
-  def ADD_Pt : ALU32_rr<(outs IntRegs:$dst),
-                         (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
-              "if ($p) $dst = add($a, $b)",
-              []>;
-
-  def ADD_Pf : ALU32_rr<(outs IntRegs:$dst),
-                         (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
-              "if (!$p) $dst = add($a, $b)",
-              []>;
+def ADD_Pf : ALU32_rr<(outs IntRegs:$dst),
+                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
+            "if (!$p) $dst = add($a, $b)",
+            []>;
+```
 
 In this step, we modify these instructions to include the information
-required by the relationship model, <tt>getPredOpcode</tt>, so that they can
+required by the relationship model, \<tt>getPredOpcode\</tt>, so that they can
 be related.
 
-.. code-block:: text
-
-  def ADD : PredRel, ALU32_rr<(outs IntRegs:$dst), (ins IntRegs:$a, IntRegs:$b),
-              "$dst = add($a, $b)",
-              [(set (i32 IntRegs:$dst), (add (i32 IntRegs:$a),
-                                             (i32 IntRegs:$b)))]> {
-    let BaseOpcode = "ADD";
-    let PredSense = "none";
-  }
-
-  def ADD_Pt : PredRel, ALU32_rr<(outs IntRegs:$dst),
-                         (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
-              "if ($p) $dst = add($a, $b)",
-              []> {
-    let BaseOpcode = "ADD";
-    let PredSense = "true";
-  }
-
-  def ADD_Pf : PredRel, ALU32_rr<(outs IntRegs:$dst),
-                         (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
-              "if (!$p) $dst = add($a, $b)",
-              []> {
-    let BaseOpcode = "ADD";
-    let PredSense = "false";
-  }
-
-Please note that all the above instructions use ``PredRel`` as a base class.
+```text
+def ADD : PredRel, ALU32_rr<(outs IntRegs:$dst), (ins IntRegs:$a, IntRegs:$b),
+            "$dst = add($a, $b)",
+            [(set (i32 IntRegs:$dst), (add (i32 IntRegs:$a),
+                                           (i32 IntRegs:$b)))]> {
+  let BaseOpcode = "ADD";
+  let PredSense = "none";
+}
+
+def ADD_Pt : PredRel, ALU32_rr<(outs IntRegs:$dst),
+                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
+            "if ($p) $dst = add($a, $b)",
+            []> {
+  let BaseOpcode = "ADD";
+  let PredSense = "true";
+}
+
+def ADD_Pf : PredRel, ALU32_rr<(outs IntRegs:$dst),
+                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
+            "if (!$p) $dst = add($a, $b)",
+            []> {
+  let BaseOpcode = "ADD";
+  let PredSense = "false";
+}
+```
+
+Please note that all the above instructions use `PredRel` as a base class.
 This is extremely important since TableGen uses it as a filter for selecting
-instructions for ``getPredOpcode`` model. Any instruction not derived from
-``PredRel`` is excluded from the analysis. ``BaseOpcode`` is another important
-field. Since it's selected as a ``RowFields`` of the model, it is required
+instructions for `getPredOpcode` model. Any instruction not derived from
+`PredRel` is excluded from the analysis. `BaseOpcode` is another important
+field. Since it's selected as a `RowFields` of the model, it is required
 to have the same value for all 3 instructions in order to be related. Next,
-``PredSense`` is used to determine their column positions by comparing its value
-with ``KeyCol`` and ``ValueCols``. If an instruction sets its ``PredSense``
+`PredSense` is used to determine their column positions by comparing its value
+with `KeyCol` and `ValueCols`. If an instruction sets its `PredSense`
 value to something not used in the relation model, it will not be assigned
 a column in the relation table.
+
diff --git a/llvm/docs/InAlloca.md b/llvm/docs/InAlloca.md
index a75f22da7964b..731123eefd714 100644
--- a/llvm/docs/InAlloca.md
+++ b/llvm/docs/InAlloca.md
@@ -1,21 +1,18 @@
-==========================================
-Design and Usage of the InAlloca Attribute
-==========================================
+# Design and Usage of the InAlloca Attribute
 
-Introduction
-============
+## Introduction
 
-The :ref:`inalloca <attr_inalloca>` attribute is designed to allow
+The {ref}`inalloca <attr_inalloca>` attribute is designed to allow
 taking the address of an aggregate argument that is being passed by
-value through memory.  Primarily, this feature is required for
-compatibility with the Microsoft C++ ABI.  Under that ABI, class
+value through memory. Primarily, this feature is required for
+compatibility with the Microsoft C++ ABI. Under that ABI, class
 instances that are passed by value are constructed directly into
-argument stack memory.  Prior to the addition of inalloca, calls in LLVM
-were indivisible instructions.  There was no way to perform intermediate
+argument stack memory. Prior to the addition of inalloca, calls in LLVM
+were indivisible instructions. There was no way to perform intermediate
 work, such as object construction, between the first stack adjustment
-and the final control transfer.  With inalloca, all arguments passed in
+and the final control transfer. With inalloca, all arguments passed in
 memory are modelled as a single alloca, which can be stored to prior to
-the call.  Unfortunately, this complicated feature comes with a large
+the call. Unfortunately, this complicated feature comes with a large
 set of restrictions designed to bound the lifetime of the argument
 memory around the call.
 
@@ -25,136 +22,130 @@ This feature may grow in the future to allow general mid-level
 optimization, but for now, it should be regarded as less efficient than
 passing by value with a copy.
 
-Intended Usage
-==============
+## Intended Usage
 
 The example below is the intended LLVM IR lowering for some C++ code
-that passes two default-constructed ``Foo`` objects to ``g`` in the
+that passes two default-constructed `Foo` objects to `g` in the
 32-bit Microsoft C++ ABI.
 
-.. code-block:: c++
-
-    // Foo is non-trivial.
-    struct Foo { int a, b; Foo(); ~Foo(); Foo(const Foo &); };
-    void g(Foo a, Foo b);
-    void f() {
-      g(Foo(), Foo());
-    }
-
-.. code-block:: text
-
-    %struct.Foo = type { i32, i32 }
-    declare void @Foo_ctor(%struct.Foo* %this)
-    declare void @Foo_dtor(%struct.Foo* %this)
-    declare void @g(<{ %struct.Foo, %struct.Foo }>* inalloca %memargs)
-
-    define void @f() {
-    entry:
-      %base = call i8* @llvm.stacksave()
-      %memargs = alloca <{ %struct.Foo, %struct.Foo }>
-      %b = getelementptr <{ %struct.Foo, %struct.Foo }>* %memargs, i32 1
-      call void @Foo_ctor(%struct.Foo* %b)
-
-      ; If a's ctor throws, we must destruct b.
-      %a = getelementptr <{ %struct.Foo, %struct.Foo }>* %memargs, i32 0
-      invoke void @Foo_ctor(%struct.Foo* %a)
-          to label %invoke.cont unwind %invoke.unwind
-
-    invoke.cont:
-      call void @g(<{ %struct.Foo, %struct.Foo }>* inalloca %memargs)
-      call void @llvm.stackrestore(i8* %base)
-      ...
-
-    invoke.unwind:
-      call void @Foo_dtor(%struct.Foo* %b)
-      call void @llvm.stackrestore(i8* %base)
-      ...
-    }
+```c++
+// Foo is non-trivial.
+struct Foo { int a, b; Foo(); ~Foo(); Foo(const Foo &); };
+void g(Foo a, Foo b);
+void f() {
+  g(Foo(), Foo());
+}
+```
+
+```text
+%struct.Foo = type { i32, i32 }
+declare void @Foo_ctor(%struct.Foo* %this)
+declare void @Foo_dtor(%struct.Foo* %this)
+declare void @g(<{ %struct.Foo, %struct.Foo }>* inalloca %memargs)
+
+define void @f() {
+entry:
+  %base = call i8* @llvm.stacksave()
+  %memargs = alloca <{ %struct.Foo, %struct.Foo }>
+  %b = getelementptr <{ %struct.Foo, %struct.Foo }>* %memargs, i32 1
+  call void @Foo_ctor(%struct.Foo* %b)
+
+  ; If a's ctor throws, we must destruct b.
+  %a = getelementptr <{ %struct.Foo, %struct.Foo }>* %memargs, i32 0
+  invoke void @Foo_ctor(%struct.Foo* %a)
+      to label %invoke.cont unwind %invoke.unwind
+
+invoke.cont:
+  call void @g(<{ %struct.Foo, %struct.Foo }>* inalloca %memargs)
+  call void @llvm.stackrestore(i8* %base)
+  ...
+
+invoke.unwind:
+  call void @Foo_dtor(%struct.Foo* %b)
+  call void @llvm.stackrestore(i8* %base)
+  ...
+}
+```
 
 To avoid stack leaks, the frontend saves the current stack pointer with
-a call to :ref:`llvm.stacksave <int_stacksave>`.  Then, it allocates the
-argument stack space with alloca and calls the default constructor.  The
+a call to {ref}`llvm.stacksave <int_stacksave>`. Then, it allocates the
+argument stack space with alloca and calls the default constructor. The
 default constructor could throw an exception, so the frontend has to
-create a landing pad.  The frontend has to destroy the already
-constructed argument ``b`` before restoring the stack pointer.  If the
-constructor does not unwind, ``g`` is called.  In the Microsoft C++ ABI,
-``g`` will destroy its arguments, and then the stack is restored in
-``f``.
+create a landing pad. The frontend has to destroy the already
+constructed argument `b` before restoring the stack pointer. If the
+constructor does not unwind, `g` is called. In the Microsoft C++ ABI,
+`g` will destroy its arguments, and then the stack is restored in
+`f`.
 
-Design Considerations
-=====================
+## Design Considerations
 
-Lifetime
---------
+### Lifetime
 
 The biggest design consideration for this feature is object lifetime.
 We cannot model the arguments as static allocas in the entry block,
 because all calls need to use the memory at the top of the stack to pass
-arguments.  We cannot vend pointers to that memory at function entry
+arguments. We cannot vend pointers to that memory at function entry
 because after code generation they will alias.
 
 The rule against allocas between argument allocations and the call site
-avoids this problem, but it creates a cleanup problem.  Cleanup and
-lifetime is handled explicitly with stack save and restore calls.  In
-the future, we may want to introduce a new construct such as ``freea``
-or ``afree`` to make it clear that this stack adjusting cleanup is less
+avoids this problem, but it creates a cleanup problem. Cleanup and
+lifetime is handled explicitly with stack save and restore calls. In
+the future, we may want to introduce a new construct such as `freea`
+or `afree` to make it clear that this stack adjusting cleanup is less
 powerful than a full stack save and restore.
 
-Nested Calls and Copy Elision
------------------------------
+### Nested Calls and Copy Elision
 
 We also want to be able to support copy elision into these argument
-slots.  This means we have to support multiple live argument
+slots. This means we have to support multiple live argument
 allocations.
 
 Consider the evaluation of:
 
-.. code-block:: c++
-
-    // Foo is non-trivial.
-    struct Foo { int a; Foo(); Foo(const &Foo); ~Foo(); };
-    Foo bar(Foo b);
-    int main() {
-      bar(bar(Foo()));
-    }
-
-In this case, we want to be able to elide copies into ``bar``'s argument
-slots.  That means we need to have more than one set of argument frames
-active at the same time.  First, we need to allocate the frame for the
+```c++
+// Foo is non-trivial.
+struct Foo { int a; Foo(); Foo(const &Foo); ~Foo(); };
+Foo bar(Foo b);
+int main() {
+  bar(bar(Foo()));
+}
+```
+
+In this case, we want to be able to elide copies into `bar`'s argument
+slots. That means we need to have more than one set of argument frames
+active at the same time. First, we need to allocate the frame for the
 outer call so we can pass it in as the hidden struct return pointer to
-the middle call.  Then we do the same for the middle call, allocating a
-frame and passing its address to ``Foo``'s default constructor.  By
-wrapping the evaluation of the inner ``bar`` with stack save and
+the middle call. Then we do the same for the middle call, allocating a
+frame and passing its address to `Foo`'s default constructor. By
+wrapping the evaluation of the inner `bar` with stack save and
 restore, we can have multiple overlapping active call frames.
 
-Callee-cleanup Calling Conventions
-----------------------------------
+### Callee-cleanup Calling Conventions
 
-Another wrinkle is the existence of callee-cleanup conventions.  On
+Another wrinkle is the existence of callee-cleanup conventions. On
 Windows, all methods and many other functions adjust the stack to clear
-the memory used to pass their arguments.  In some sense, this means that
-the allocas are automatically cleared by the call.  However, LLVM
+the memory used to pass their arguments. In some sense, this means that
+the allocas are automatically cleared by the call. However, LLVM
 instead models this as a write of undef to all of the inalloca values
-passed to the call instead of a stack adjustment.  Frontends should
+passed to the call instead of a stack adjustment. Frontends should
 still restore the stack pointer to avoid a stack leak.
 
-Exceptions
-----------
+### Exceptions
 
-There is also the possibility of an exception.  If argument evaluation
+There is also the possibility of an exception. If argument evaluation
 or copy construction throws an exception, the landing pad must do
 cleanup, which includes adjusting the stack pointer to avoid a stack
-leak.  This means the cleanup of the stack memory cannot be tied to the
-call itself.  There needs to be a separate IR-level instruction that can
+leak. This means the cleanup of the stack memory cannot be tied to the
+call itself. There needs to be a separate IR-level instruction that can
 perform independent cleanup of arguments.
 
-Efficiency
-----------
+### Efficiency
 
 Eventually, it should be possible to generate efficient code for this
-construct.  In particular, using inalloca should not require a base
-pointer.  If the backend can prove that all points in the CFG only have
+construct. In particular, using inalloca should not require a base
+pointer. If the backend can prove that all points in the CFG only have
 one possible stack level, then it can address the stack directly from
-the stack pointer.  While this is not yet implemented, the plan is that
+the stack pointer. While this is not yet implemented, the plan is that
 the inalloca attribute should not change much, but the frontend IR
 generation recommendations may change.
+
diff --git a/llvm/docs/InterfaceExportAnnotations.md b/llvm/docs/InterfaceExportAnnotations.md
index ec1f907b5f645..1589a7a5569fa 100644
--- a/llvm/docs/InterfaceExportAnnotations.md
+++ b/llvm/docs/InterfaceExportAnnotations.md
@@ -1,18 +1,18 @@
-LLVM Interface Export Annotations
-=================================
+# LLVM Interface Export Annotations
+
 Symbols that are part of LLVM's public interface must be explicitly annotated
 to support shared library builds with hidden default symbol visibility. This
 document provides background and guidelines for annotating the codebase.
 
-LLVM Shared Library
--------------------
+## LLVM Shared Library
+
 LLVM builds as a static library by default, but it can also be built as a shared
 library with the following configuration:
 
-::
-
-   LLVM_BUILD_LLVM_DYLIB=On
-   LLVM_LINK_LLVM_DYLIB=On
+```
+LLVM_BUILD_LLVM_DYLIB=On
+LLVM_LINK_LLVM_DYLIB=On
+```
 
 There are three shared library executable formats we're interested in: PE
 Dynamic Link Library (.dll) on Windows, Mach-O Shared Object (.dylib) on Apple
@@ -24,161 +24,158 @@ default -- the same as when building a static library. However, when building a
 DLL for Windows, the situation is more complex:
 
 - Symbols are not exported from a DLL by default. Symbols must be annotated with
-  ``__declspec(dllexport)`` when building the library to be externally visible.
-
+  `__declspec(dllexport)` when building the library to be externally visible.
 - Symbols imported from a Windows DLL should generally be annotated with
-  ``__declspec(dllimport)`` when compiling clients.
-
+  `__declspec(dllimport)` when compiling clients.
 - A single Windows DLL can export a maximum of 65,535 symbols.
 
 Because of the requirements for Windows DLLs, additional work must be done to
 ensure the proper set of public symbols is exported and visible to clients.
 
-Annotation Macros
------------------
+## Annotation Macros
+
 The distinct DLL import and export annotations required for Windows DLLs
 typically lead developers to define a preprocessor macro for annotating
 exported symbols in header public files. The custom macro resolves to the
 **export** annotation when building the library and the **import** annotation
 when building the client.
 
-We have defined the ``LLVM_ABI`` macro in `llvm/Support/Compiler.h
-<https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Support/Compiler.h#L152>`__
+We have defined the `LLVM_ABI` macro in [llvm/Support/Compiler.h](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Support/Compiler.h#L152)
 for this purpose:
 
-.. code:: cpp
-
-   #if defined(LLVM_EXPORTS)
-   #define LLVM_ABI __declspec(dllexport)
-   #else
-   #define LLVM_ABI __declspec(dllimport)
-   #endif
+```cpp
+#if defined(LLVM_EXPORTS)
+#define LLVM_ABI __declspec(dllexport)
+#else
+#define LLVM_ABI __declspec(dllimport)
+#endif
+```
 
 Windows DLL symbol visibility requirements are approximated on ELF and Mach-O
 shared library builds by setting default symbol visibility to hidden
-(``-fvisibility-default=hidden``) when building with the following
+(`-fvisibility-default=hidden`) when building with the following
 configuration:
 
-::
+```
+LLVM_BUILD_LLVM_DYLIB_VIS=On
+```
 
-   LLVM_BUILD_LLVM_DYLIB_VIS=On
-
-For an ELF or Mach-O platform with this setting, the ``LLVM_ABI`` macro is
+For an ELF or Mach-O platform with this setting, the `LLVM_ABI` macro is
 defined to override the default hidden symbol visibility:
 
-.. code:: cpp
-
-   #define LLVM_ABI __attribute__((visibility("default")))
+```cpp
+#define LLVM_ABI __attribute__((visibility("default")))
+```
 
-In addition to ``LLVM_ABI``, there are a few other macros for use in less
+In addition to `LLVM_ABI`, there are a few other macros for use in less
 common cases described below.
 
 Export macros are used to annotate symbols only within their intended shared
 library. This is necessary because of the way Windows handles import/export
 annotations.
 
-For example, ``LLVM_ABI`` resolves to ``__declspec(dllexport)`` only when
+For example, `LLVM_ABI` resolves to `__declspec(dllexport)` only when
 building source that is part of the LLVM shared library (e.g. source under
-``llvm-project/llvm``). If ``LLVM_ABI`` were incorrectly used to annotate a
+`llvm-project/llvm`). If `LLVM_ABI` were incorrectly used to annotate a
 symbol from a different LLVM project (such as Clang) it would always resolve to
-``__declspec(dllimport)`` and the symbol would not be properly exported.
+`__declspec(dllimport)` and the symbol would not be properly exported.
+
+## How to Annotate Symbols
+
+### Functions
 
-How to Annotate Symbols
------------------------
-Functions
-~~~~~~~~~
 Exported function declarations in header files must be annotated with
-``LLVM_ABI``.
+`LLVM_ABI`.
 
-.. code:: cpp
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   #include "llvm/Support/Compiler.h"
+LLVM_ABI void exported_function(int a, int b);
+```
 
-   LLVM_ABI void exported_function(int a, int b);
+### Global Variables
 
-Global Variables
-~~~~~~~~~~~~~~~~
-Exported global variables must be annotated with ``LLVM_ABI`` at their
-``extern`` declarations.
+Exported global variables must be annotated with `LLVM_ABI` at their
+`extern` declarations.
 
-.. code:: cpp
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   #include "llvm/Support/Compiler.h"
+LLVM_ABI extern int exported_global_variable;
+```
 
-   LLVM_ABI extern int exported_global_variable;
+### Classes, Structs, and Unions
 
-Classes, Structs, and Unions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Classes, structs, and unions can be annotated with ``LLVM_ABI`` at their
+Classes, structs, and unions can be annotated with `LLVM_ABI` at their
 declaration, but this option is generally discouraged because it will
-export every class member, vtable, and type information. Instead, ``LLVM_ABI``
+export every class member, vtable, and type information. Instead, `LLVM_ABI`
 should be applied to individual class members that require export.
 
 In the most common case, public and protected methods without a body in the
-class declaration must be annotated with ``LLVM_ABI``.
+class declaration must be annotated with `LLVM_ABI`.
 
-.. code:: cpp
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   #include "llvm/Support/Compiler.h"
+class ExampleClass {
+public:
+  // Public methods defined externally must be annotated.
+  LLVM_ABI int sourceDefinedPublicMethod(int a, int b);
 
-   class ExampleClass {
-   public:
-     // Public methods defined externally must be annotated.
-     LLVM_ABI int sourceDefinedPublicMethod(int a, int b);
+  // Methods defined in the class definition do not need annotation.
+  int headerDefinedPublicMethod(int a, int b) {
+    return a + b;
+  }
 
-     // Methods defined in the class definition do not need annotation.
-     int headerDefinedPublicMethod(int a, int b) {
-       return a + b;
-     }
+  // Constructors and destructors must be annotated if defined externally.
+  ExampleClass() {}
+  LLVM_ABI ~ExampleClass();
 
-     // Constructors and destructors must be annotated if defined externally.
-     ExampleClass() {}
-     LLVM_ABI ~ExampleClass();
-
-     // Public static methods defined externally must be annotated.
-     LLVM_ABI static int sourceDefinedPublicStaticMethod(int a, int b);
-   };
+  // Public static methods defined externally must be annotated.
+  LLVM_ABI static int sourceDefinedPublicStaticMethod(int a, int b);
+};
+```
 
 Additionally, public and protected static fields that are not initialized at
-declaration must be annotated with ``LLVM_ABI``.
-
-.. code:: cpp
+declaration must be annotated with `LLVM_ABI`.
 
-   #include "llvm/Support/Compiler.h"
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   class ExampleClass {
-   public:
-     // Public static fields defined externally must be annotated.
-     LLVM_ABI static int mutableStaticField;
-     LLVM_ABI static const int constStaticField;
+class ExampleClass {
+public:
+  // Public static fields defined externally must be annotated.
+  LLVM_ABI static int mutableStaticField;
+  LLVM_ABI static const int constStaticField;
 
-     // Static members initialized at declaration do not need to be annotated.
-     static const int initializedConstStaticField = 0;
-     static constexpr int initializedConstexprStaticField = 0;
-   };
+  // Static members initialized at declaration do not need to be annotated.
+  static const int initializedConstStaticField = 0;
+  static constexpr int initializedConstexprStaticField = 0;
+};
+```
 
-Private methods may also require ``LLVM_ABI`` annotation. This situation occurs
+Private methods may also require `LLVM_ABI` annotation. This situation occurs
 when a method defined in a header calls the private method. The private method
 call may be from within the class or a friend class or method.
 
-.. code:: cpp
-
-   #include "llvm/Support/Compiler.h"
-
-   class ExampleClass {
-   private:
-     // Private methods must be annotated if referenced by a public method defined a
-     // header file.
-     LLVM_ABI int privateMethod(int a, int b);
-
-   public:
-     // Inlineable method defined in the class definition calls a private method
-     // defined externally. If the private method is not annotated for export, this
-     // method will fail to link.
-     int publicMethod(int a, int b) {
-       return privateMethod(a, b);
-     }
-   };
+```cpp
+#include "llvm/Support/Compiler.h"
+
+class ExampleClass {
+private:
+  // Private methods must be annotated if referenced by a public method defined a
+  // header file.
+  LLVM_ABI int privateMethod(int a, int b);
+
+public:
+  // Inlineable method defined in the class definition calls a private method
+  // defined externally. If the private method is not annotated for export, this
+  // method will fail to link.
+  int publicMethod(int a, int b) {
+    return privateMethod(a, b);
+  }
+};
+```
 
 There are less common cases where you may also need to annotate an inline
 function even though it is fully defined in a header. Annotating an inline
@@ -186,215 +183,215 @@ function for export does not prevent it being inlined into client code. However,
 it does ensure there is a single, stable address for the function exported from
 the shared library.
 
-.. code:: cpp
-
-   #include "llvm/Support/Compiler.h"
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   // Annotate the function so it is exported from the library at a fixed
-   // address.
-   LLVM_ABI inline int inlineFunction(int a, int b) {
-     return a + b;
-   }
+// Annotate the function so it is exported from the library at a fixed
+// address.
+LLVM_ABI inline int inlineFunction(int a, int b) {
+  return a + b;
+}
+```
 
 Similarly, if a stable pointer-to-member function address is required for a
 method in a C++ class, it may be annotated for export.
 
-.. code:: cpp
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   #include "llvm/Support/Compiler.h"
+class ExampleClass {
+public:
+  // Annotate the method so it is exported from the library at a fixed
+  // address.
+  LLVM_ABI inline int inlineMethod(int a, int b) {
+    return a + b;
+  }
+};
+```
 
-   class ExampleClass {
-   public:
-     // Annotate the method so it is exported from the library at a fixed
-     // address.
-     LLVM_ABI inline int inlineMethod(int a, int b) {
-       return a + b;
-     }
-   };
+:::{note}
+When an inline function is annotated for export, the header containing the
+function definition **must** be included by at least one of the library's
+source files or the function will never be compiled with the export
+annotation.
+:::
 
-.. note::
+### Friend Functions
 
-   When an inline function is annotated for export, the header containing the
-   function definition **must** be included by at least one of the library's
-   source files or the function will never be compiled with the export
-   annotation.
-
-Friend Functions
-~~~~~~~~~~~~~~~~
 Friend functions declared in a class, struct or union must be annotated with
-``LLVM_ABI`` if the corresponding function declaration is annotated with
-``LLVM_ABI``. This requirement applies even when the class containing the friend
-declaration is annotated with ``LLVM_ABI``.
-
-.. code:: cpp
+`LLVM_ABI` if the corresponding function declaration is annotated with
+`LLVM_ABI`. This requirement applies even when the class containing the friend
+declaration is annotated with `LLVM_ABI`.
 
-   #include "llvm/Support/Compiler.h"
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   // An exported function that has friend access to ExampleClass internals.
-   LLVM_ABI int friend_function(ExampleClass &obj);
+// An exported function that has friend access to ExampleClass internals.
+LLVM_ABI int friend_function(ExampleClass &obj);
 
-   class ExampleClass {
-     // Friend declaration of a function must be annotated the same as the actual
-     // function declaration.
-     LLVM_ABI friend int friend_function(ExampleClass &obj);
-   };
+class ExampleClass {
+  // Friend declaration of a function must be annotated the same as the actual
+  // function declaration.
+  LLVM_ABI friend int friend_function(ExampleClass &obj);
+};
+```
 
-.. note::
+:::{note}
+Annotating the friend declaration avoids an “inconsistent dll linkage”
+compiler error when building a DLL for Windows.
+:::
 
-   Annotating the friend declaration avoids an “inconsistent dll linkage”
-   compiler error when building a DLL for Windows.
+### Virtual Table and Type Info
 
-Virtual Table and Type Info
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Classes and structs with exported virtual methods, including child classes that
 export overridden virtual methods, must also export their vtable for ELF and
 Mach-O builds. This can be achieved by annotating the class rather than
 individual class members.
 
 The general rule here is to annotate at the class level if any out-of-line
-method is declared ``virtual`` or ``override``.
-
-.. code:: cpp
-
-   #include "llvm/Support/Compiler.h"
-
-   // Annotating the class exports vtable and type information as well as all
-   // class members.
-   class LLVM_ABI ParentClass {
-   public:
-     virtual int virtualMethod(int a, int b);
-     virtual int anotherVirtualMethod(int a, int b);
-     virtual ~ParentClass();
-   };
-
-   class LLVM_ABI ChildClass : public ParentClass {
-   public:
-     // Inline method override does not require the class be annotated.
-     int virtualMethod(int a, int b) override {
-       return ParentClass::virtualMethod(a, b);
-     }
-
-     // Overriding a virtual method from the parent requires the class be
-     // annotated.
-     int pureVirtualMethod(int a, int b) override;
-
-     ~ChildClass();
-   };
-
-.. note::
-
-   If a class is annotated, none of its members may be annotated. If class- and
-   member-level annotations are combined on a class, it will fail compilation on
-   Windows.
-
-Compilation Errors
-++++++++++++++++++
-Annotating a class with ``LLVM_ABI`` causes the compiler to fully instantiate
+method is declared `virtual` or `override`.
+
+```cpp
+#include "llvm/Support/Compiler.h"
+
+// Annotating the class exports vtable and type information as well as all
+// class members.
+class LLVM_ABI ParentClass {
+public:
+  virtual int virtualMethod(int a, int b);
+  virtual int anotherVirtualMethod(int a, int b);
+  virtual ~ParentClass();
+};
+
+class LLVM_ABI ChildClass : public ParentClass {
+public:
+  // Inline method override does not require the class be annotated.
+  int virtualMethod(int a, int b) override {
+    return ParentClass::virtualMethod(a, b);
+  }
+
+  // Overriding a virtual method from the parent requires the class be
+  // annotated.
+  int pureVirtualMethod(int a, int b) override;
+
+  ~ChildClass();
+};
+```
+
+:::{note}
+If a class is annotated, none of its members may be annotated. If class- and
+member-level annotations are combined on a class, it will fail compilation on
+Windows.
+:::
+
+#### Compilation Errors
+
+Annotating a class with `LLVM_ABI` causes the compiler to fully instantiate
 the class at compile time. This requires exporting every method that could be
 potentially used by a client even though no existing clients may actually use
 them. This can cause compilation errors that were not previously present.
 
 The most common type of error occurs when the compiler attempts to instantiate
 and export a class' implicit copy constructor and copy assignment operator. If
-the class contains move-only members that cannot be copied (``std::unique_ptr``
+the class contains move-only members that cannot be copied (`std::unique_ptr`
 for example), the compiler will fail to instantiate these implicit
 methods.
 
 This problem is easily addressed by explicitly deleting the class' copy
 constructor and copy assignment operator:
 
-.. code:: cpp
-
-   #include "llvm/Support/Compiler.h"
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   class LLVM_ABI ExportedClass {
-   public:
-     ExportedClass() = default;
+class LLVM_ABI ExportedClass {
+public:
+  ExportedClass() = default;
 
-     // Explicitly delete the copy constructor and assignment operator.
-     ExportedClass(ExportedClass const&) = delete;
-     ExportedClass& operator=(ExportedClass const&) = delete;
-   };
+  // Explicitly delete the copy constructor and assignment operator.
+  ExportedClass(ExportedClass const&) = delete;
+  ExportedClass& operator=(ExportedClass const&) = delete;
+};
+```
 
 We know this modification is harmless because any clients attempting to use
 these methods already would fail to compile. For a more detailed explanation,
-see `this Microsoft dev blog
-<https://devblogs.microsoft.com/oldnewthing/20190927-00/?p=102932>`__.
+see [this Microsoft dev blog](https://devblogs.microsoft.com/oldnewthing/20190927-00/?p=102932).
+
+### Templates
 
-Templates
-~~~~~~~~~
 Most template classes are entirely header-defined and do not need to be exported
 because they will be instantiated and compiled into the client as needed. Such
 template classes require no export annotations. However, there are some less
 common cases where annotations are required for templates.
 
-Specialized Template Functions
-++++++++++++++++++++++++++++++
+#### Specialized Template Functions
+
 As with any other exported function, an exported specialization of a template
 function not defined in a header file must have its declaration annotated with
-``LLVM_ABI``.
+`LLVM_ABI`.
 
-.. code:: cpp
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   #include "llvm/Support/Compiler.h"
+template <typename T> T templateMethod(T a, T b) {
+  return a + b;
+}
 
-   template <typename T> T templateMethod(T a, T b) {
-     return a + b;
-   }
-
-   // The explicitly specialized definition of templateMethod for int is located in
-   // a source file. This declaration must be annotated with LLVM_ABI to export it.
-   template <> LLVM_ABI int templateMethod(int a, int b);
+// The explicitly specialized definition of templateMethod for int is located in
+// a source file. This declaration must be annotated with LLVM_ABI to export it.
+template <> LLVM_ABI int templateMethod(int a, int b);
+```
 
 Similarly, an exported specialization of a method in a template class must have
-its declaration annotated with ``LLVM_ABI``.
+its declaration annotated with `LLVM_ABI`.
 
-.. code:: cpp
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   #include "llvm/Support/Compiler.h"
+template <typename T> class TemplateClass {
+public:
+  int method(int a, int b) {
+    return a + b;
+  }
+};
 
-   template <typename T> class TemplateClass {
-   public:
-     int method(int a, int b) {
-       return a + b;
-     }
-   };
+// The explicitly specialized definition of method for int is defined in a
+// source file. The declaration must be annotated with LLVM_ABI to export it.
+template <> LLVM_ABI int TemplateStruct<int>::method(int a, int b);
+```
 
-   // The explicitly specialized definition of method for int is defined in a
-   // source file. The declaration must be annotated with LLVM_ABI to export it.
-   template <> LLVM_ABI int TemplateStruct<int>::method(int a, int b);
+#### Explicitly Instantiated Template Classes
 
-Explicitly Instantiated Template Classes
-++++++++++++++++++++++++++++++++++++++++
 Explicitly instantiated template classes must be annotated with
 template-specific annotations at both declaration and definition.
 
 An extern template instantiation in a header file must be annotated with
-``LLVM_TEMPLATE_ABI``. This will typically be located in a header file.
-
-.. code:: cpp
+`LLVM_TEMPLATE_ABI`. This will typically be located in a header file.
 
-   #include "llvm/Support/Compiler.h"
+```cpp
+#include "llvm/Support/Compiler.h"
 
-   template <typename T> class TemplateClass {
-   public:
-     TemplateClass(T val) : val_(val) {}
+template <typename T> class TemplateClass {
+public:
+  TemplateClass(T val) : val_(val) {}
 
-     T get() const { return val_;  }
+  T get() const { return val_;  }
 
-   private:
-     const T val_;
-   };
+private:
+  const T val_;
+};
 
-   // Explicitly instantiate and export TempalateClass for int type.
-   extern template class LLVM_TEMPLATE_ABI TemplateClass<int>;
+// Explicitly instantiate and export TempalateClass for int type.
+extern template class LLVM_TEMPLATE_ABI TemplateClass<int>;
+```
 
 The corresponding definition of the template instantiation must be annotated
-with ``LLVM_EXPORT_TEMPLATE``. This will typically be located in a source file.
+with `LLVM_EXPORT_TEMPLATE`. This will typically be located in a source file.
 
-.. code:: cpp
+```cpp
+#include "TemplateClass.h"
 
-   #include "TemplateClass.h"
+// Explicitly instantiate and export TempalateClass for int type.
+template class LLVM_EXPORT_TEMPLATE TemplateClass<int>;
+```
 
-   // Explicitly instantiate and export TempalateClass for int type.
-   template class LLVM_EXPORT_TEMPLATE TemplateClass<int>;
diff --git a/llvm/docs/KernelInfo.md b/llvm/docs/KernelInfo.md
index 186dd5d1b52d0..c339770704a15 100644
--- a/llvm/docs/KernelInfo.md
+++ b/llvm/docs/KernelInfo.md
@@ -1,61 +1,57 @@
-==========
-KernelInfo
-==========
+# KernelInfo
 
+## Introduction
 
-Introduction
-============
-
-This LLVM IR pass reports various statistics for code compiled for GPUs.  The
+This LLVM IR pass reports various statistics for code compiled for GPUs. The
 goal of these statistics is to help identify bad code patterns and ways to
-mitigate them.  The pass operates at the LLVM IR level so that it can, in
+mitigate them. The pass operates at the LLVM IR level so that it can, in
 theory, support any LLVM-based compiler for programming languages supporting
 GPUs.
 
 By default, the pass runs at the end of LTO, and options like
-``-Rpass=kernel-info`` enable its remarks.  Example ``opt`` and ``clang``
+`-Rpass=kernel-info` enable its remarks. Example `opt` and `clang`
 command lines appear in the next section.
 
 Remarks include summary statistics (e.g., total size of static allocas) and
-individual occurrences (e.g., source location of each alloca).  Examples of the
+individual occurrences (e.g., source location of each alloca). Examples of the
 output appear in tests in `llvm/test/Analysis/KernelInfo`.
 
-Example Command Lines
-=====================
+## Example Command Lines
 
 To analyze a C program as it appears to an LLVM GPU backend at the end of LTO:
 
-.. code-block:: shell
-
-  $ clang -O2 -g -fopenmp --offload-arch=native test.c -foffload-lto \
-      -Rpass=kernel-info
+```shell
+$ clang -O2 -g -fopenmp --offload-arch=native test.c -foffload-lto \
+    -Rpass=kernel-info
+```
 
 To analyze specified LLVM IR, perhaps previously generated by something like
-``clang -save-temps -g -fopenmp --offload-arch=native test.c``:
-
-.. code-block:: shell
+`clang -save-temps -g -fopenmp --offload-arch=native test.c`:
 
-  $ opt -disable-output test-openmp-nvptx64-nvidia-cuda-sm_70.bc \
-      -pass-remarks=kernel-info -passes=kernel-info
+```shell
+$ opt -disable-output test-openmp-nvptx64-nvidia-cuda-sm_70.bc \
+    -pass-remarks=kernel-info -passes=kernel-info
+```
 
-When specifying an LLVM pass pipeline on the command line, ``kernel-info`` still
-runs at the end of LTO by default.  ``-no-kernel-info-end-lto`` disables that
-behavior so you can position ``kernel-info`` explicitly:
+When specifying an LLVM pass pipeline on the command line, `kernel-info` still
+runs at the end of LTO by default. `-no-kernel-info-end-lto` disables that
+behavior so you can position `kernel-info` explicitly:
 
-.. code-block:: shell
+```shell
+$ clang -O2 -g -fopenmp --offload-arch=native test.c -foffload-lto \
+    -Rpass=kernel-info \
+    -Xoffload-linker --lto-newpm-passes='lto<O2>'
 
-  $ clang -O2 -g -fopenmp --offload-arch=native test.c -foffload-lto \
-      -Rpass=kernel-info \
-      -Xoffload-linker --lto-newpm-passes='lto<O2>'
+$ clang -O2 -g -fopenmp --offload-arch=native test.c -foffload-lto \
+    -Rpass=kernel-info -mllvm -no-kernel-info-end-lto \
+    -Xoffload-linker --lto-newpm-passes='module(kernel-info),lto<O2>'
 
-  $ clang -O2 -g -fopenmp --offload-arch=native test.c -foffload-lto \
-      -Rpass=kernel-info -mllvm -no-kernel-info-end-lto \
-      -Xoffload-linker --lto-newpm-passes='module(kernel-info),lto<O2>'
+$ opt -disable-output test-openmp-nvptx64-nvidia-cuda-sm_70.bc \
+    -pass-remarks=kernel-info \
+    -passes='lto<O2>'
 
-  $ opt -disable-output test-openmp-nvptx64-nvidia-cuda-sm_70.bc \
-      -pass-remarks=kernel-info \
-      -passes='lto<O2>'
+$ opt -disable-output test-openmp-nvptx64-nvidia-cuda-sm_70.bc \
+    -pass-remarks=kernel-info -no-kernel-info-end-lto \
+    -passes='module(kernel-info),lto<O2>'
+```
 
-  $ opt -disable-output test-openmp-nvptx64-nvidia-cuda-sm_70.bc \
-      -pass-remarks=kernel-info -no-kernel-info-end-lto \
-      -passes='module(kernel-info),lto<O2>'
diff --git a/llvm/docs/LoopFusion.md b/llvm/docs/LoopFusion.md
index 3309b55443a67..03273febe1a6b 100644
--- a/llvm/docs/LoopFusion.md
+++ b/llvm/docs/LoopFusion.md
@@ -1,9 +1,6 @@
-====================================================
-Loop Fusion in LLVM
-====================================================
+# Loop Fusion in LLVM
 
-1. Introduction
-===============
+## 1. Introduction
 
 Loop fusion (also called loop jamming) is a compiler optimization that
 merges two adjacent loops into a single loop, provided the
@@ -17,16 +14,15 @@ subsequent optimizations such as instruction scheduling and register
 allocation.
 
 LLVM's implementation resides in
-``llvm/lib/Transforms/Scalar/LoopFuse.cpp`` and is based on
+`llvm/lib/Transforms/Scalar/LoopFuse.cpp` and is based on
 Christopher Barton's MSc thesis, *"Code Transformations to Augment the
 Scope of Loop Fusion in a Production Compiler"*. The pass operates on
-LLVM IR, leveraging several core analysis frameworks -- 
+LLVM IR, leveraging several core analysis frameworks --
 Scalar Evolution (SCEV), Dependence Analysis
 (DA), and Dominator/Post-Dominator Trees -- to determine legality and
 perform the CFG rewiring that fuses two loops into one.
 
-2. Prerequisite Concepts
-========================
+## 2. Prerequisite Concepts
 
 The fusion pass relies on several standard LLVM loop concepts that
 are documented elsewhere: simplified loop form, rotated loop form,
@@ -45,16 +41,14 @@ accesses. A dependence from instruction S1 to S2 is characterized by:
 - **Output dependence**: Both S1 and S2 write (write-after-write).
 
 Each dependence carries a *direction vector* at each loop nesting
-level, indicating whether the source iteration is less than (``<``),
-equal to (``=``), or greater than (``>``) the sink iteration at that
-level. A dependence with a ``>`` component at the current loop level
+level, indicating whether the source iteration is less than (`<`),
+equal to (`=`), or greater than (`>`) the sink iteration at that
+level. A dependence with a `>` component at the current loop level
 represents a *backward loop-carried dependence* (also called a
 negative-distance dependence). Such dependences are the critical
 hazard that loop fusion must respect.
 
-
-3. High-Level Algorithm
-=======================
+## 3. High-Level Algorithm
 
 The pass operates in a top-down, level-by-level fashion over the loop
 nest tree. At each nesting depth, it:
@@ -64,7 +58,7 @@ nest tree. At each nesting depth, it:
    *control-flow equivalent, strictly adjacent* loops sorted in
    dominance order.
 2. **Attempts pairwise fusion**: Walks each chain linearly, testing
-   every consecutive pair ``(FC0, FC1)`` against the four legality
+   every consecutive pair `(FC0, FC1)` against the four legality
    conditions. If all conditions hold, the pair is fused and replaced
    by the fused loop in the chain, which is then considered for
    further fusion with its successor.
@@ -74,16 +68,13 @@ nest tree. At each nesting depth, it:
 This strategy means outermost loops are fused first. Fusing inner
 loops is handled in subsequent iterations of the outer while-loop.
 
-
-4. Phase 1: Candidate Collection
-=================================
+## 4. Phase 1: Candidate Collection
 
 For each group of sibling loops (loops sharing a parent) at the
 current depth, the pass gathers the loops that are eligible for
 fusion.
 
-4.1 Eligibility Check
-----------------------
+### 4.1 Eligibility Check
 
 Each loop is first scanned for disqualifying properties. A loop is
 rejected immediately if any of its blocks has its address taken, if
@@ -110,8 +101,7 @@ requirements:
 If any check fails, the loop is discarded and an optimization remark
 is emitted explaining why.
 
-4.2 Grouping by Adjacency
----------------------------
+### 4.2 Grouping by Adjacency
 
 Eligible loops are partitioned into ordered chains based on *strict
 adjacency*. Two loops FC0 and FC1 are strictly adjacent if:
@@ -128,16 +118,13 @@ new chain is started. Because the input loops are already supplied in
 dominance (program) order, this single-pass grouping correctly
 partitions eligible loops into maximal chains of adjacent loops.
 
-
-5. Phase 2: Pairwise Fusion Attempts
-=====================================
+## 5. Phase 2: Pairwise Fusion Attempts
 
 The pass iterates over each chain and attempts to fuse consecutive
-pairs ``(FC0, FC1)``. The following legality conditions are checked
+pairs `(FC0, FC1)`. The following legality conditions are checked
 in order, with early exit on failure.
 
-5.1 Condition 1: Identical Trip Counts (Conformance)
-------------------------------------------------------
+### 5.1 Condition 1: Identical Trip Counts (Conformance)
 
 The conformance check uses SCEV to retrieve the backedge-taken count
 of both loops. If the SCEV expressions are identical (pointer equality
@@ -146,15 +133,14 @@ after canonicalization), the loops are conforming.
 If they differ but both are small constants, the pass computes the
 arithmetic difference. If the first loop has more iterations than the
 second, and the difference does not exceed the command-line limit
-``-loop-fusion-peel-max-count`` (default 0), the pass marks the pair
+`-loop-fusion-peel-max-count` (default 0), the pass marks the pair
 as eligible for peeling. Peeling the first loop by the difference
 will equalize the trip counts.
 
 The current implementation does not support the case where the second
 loop has more iterations than the first.
 
-5.2 Condition 2: Compatible Guard Structure
---------------------------------------------
+### 5.2 Condition 2: Compatible Guard Structure
 
 Both loops must be either both guarded or both unguarded. If one is
 guarded and the other is not, fusion is rejected. When both are
@@ -172,8 +158,7 @@ that FC1's guard block instructions can be safely moved before FC0's
 guard block terminator. These moves reuse LLVM's generic code-motion
 safety helpers.
 
-5.3 Condition 3: No Negative-Distance Dependencies
-----------------------------------------------------
+### 5.3 Condition 3: No Negative-Distance Dependencies
 
 The dependence check is the most involved legality analysis. It
 examines all pairs of memory accesses where at least one is a write:
@@ -191,8 +176,8 @@ this.
 
 For each memory pair, the pass invokes one of three dependence
 analysis strategies, selectable via
-``--loop-fusion-dependence-analysis``. The default is DA-based
-analysis (``da``); the SCEV-based and combined modes are retained as
+`--loop-fusion-dependence-analysis`. The default is DA-based
+analysis (`da`); the SCEV-based and combined modes are retained as
 opt-ins. The SCEV-based path is expected to be removed in a future
 change.
 
@@ -209,13 +194,13 @@ exists, fusion is safe. If a dependence exists, the pass examines its
 direction vector:
 
 1. At outer loop levels (levels above the current fusion level): if
-   any level has a direction that excludes equality (``EQ``), the
+   any level has a direction that excludes equality (`EQ`), the
    outer indices differ and the dependence does not constrain fusion
    at the current level.
-2. At the current level: if the direction excludes ``GT``
+2. At the current level: if the direction excludes `GT`
    (greater-than), there is no backward loop-carried dependence, and
-   fusion is safe. For example, a pure ``LT`` direction indicates a
-   forward dependence like ``A[i] = ...; ... = A[i-1]``, which
+   fusion is safe. For example, a pure `LT` direction indicates a
+   forward dependence like `A[i] = ...; ... = A[i-1]`, which
    remains valid after fusion.
 3. Loop-invariant (scalar) non-anti dependences at the current level
    are also safe.
@@ -224,8 +209,7 @@ direction vector:
 Accepts the pair if either SCEV-based or DA-based analysis approves
 it.
 
-5.4 Condition 4: Empty or Movable Preheader
----------------------------------------------
+### 5.4 Condition 4: Empty or Movable Preheader
 
 FC1's preheader must be empty (containing only the terminator branch)
 or all its instructions must be safely movable. The preheader-motion
@@ -246,37 +230,32 @@ FC0's preheader) or sinkable (into FC1's body after the fused loop):
 If any instruction is neither hoistable nor sinkable, fusion is
 abandoned for this pair.
 
-5.5 Profitability
-------------------
+### 5.5 Profitability
 
 The profitability check currently always reports that fusion is
 beneficial. This is intentional for testing coverage and is expected
 to evolve to include cost-model heuristics (e.g., register pressure
 estimation, cache footprint analysis) in the future.
 
-
-6. Phase 3: The Fusion Transformation
-======================================
+## 6. Phase 3: The Fusion Transformation
 
 Once all legality checks pass, the transformation proceeds in two
 stages: an optional peeling step and the actual CFG rewiring.
 
-6.1 Loop Peeling (Optional)
------------------------------
+### 6.1 Loop Peeling (Optional)
 
-If the trip counts differ by a constant ``d``, the pass peels ``d``
-iterations from FC0. This extracts the first ``d`` iterations as
+If the trip counts differ by a constant `d`, the pass peels `d`
+iterations from FC0. This extracts the first `d` iterations as
 straight-line code before the loop, so the remaining loop has the
 same trip count as FC1. After peeling:
 
-1. The post-dominator tree is recalculated. 
+1. The post-dominator tree is recalculated.
 2. FC0's cached block pointers are refreshed.
 3. The peeled iteration blocks' branches are rewritten to remove edges
    to FC1's preheader, ensuring FC0's entry block still dominates
    FC1's entry block.
 
-6.2 CFG Rewiring (Non-Guarded Loops)
---------------------------------------
+### 6.2 CFG Rewiring (Non-Guarded Loops)
 
 For non-guarded loops, the CFG transformation performs these steps:
 
@@ -288,7 +267,7 @@ For non-guarded loops, the CFG transformation performs these steps:
    targets FC1's header directly.
 
 3. **Delete FC1's preheader**: It has no predecessors left; replace
-   its terminator with ``unreachable``.
+   its terminator with `unreachable`.
 
 4. **Move PHI nodes**: All PHI nodes from FC1's header are moved to
    FC0's header. If a PHI has no uses, it is deleted.
@@ -297,7 +276,7 @@ For non-guarded loops, the CFG transformation performs these steps:
    not the latch (a rare case given the rotated form requirement),
    new PHI nodes are inserted in FC1's header. These select the
    loop-carried value from FC0 when arriving via FC0's latch, or
-   ``poison`` when arriving via FC0's exiting block (which means the
+   `poison` when arriving via FC0's exiting block (which means the
    loop is exiting and the value is dead).
 
 6. **Reconnect latches**:
@@ -328,8 +307,7 @@ The resulting fused loop has:
 - FC1's latch as its latch.
 - FC1's exit block as its exit block.
 
-6.3 CFG Rewiring (Guarded Loops)
-----------------------------------
+### 6.3 CFG Rewiring (Guarded Loops)
 
 The guarded-loop fusion path handles the additional complexity of
 guard branches and exit blocks:
@@ -342,8 +320,7 @@ guard branches and exit blocks:
 5. The latch rewiring and block transfer proceed identically to the
    non-guarded case.
 
-6.4 Post-Fusion Bookkeeping
------------------------------
+### 6.4 Post-Fusion Bookkeeping
 
 After fusion, the fused loop replaces the original pair in the chain
 and becomes the new left operand for the next pairwise attempt. This
@@ -353,17 +330,14 @@ fusible, A+B is fused first, then (A+B)+C is attempted.
 FC1's loop is also recorded as removed so that it is skipped when the
 pass descends to inner nesting levels.
 
-
-7. Limitations
-===============
+## 7. Limitations
 
 The current implementation is purely opportunistic: it fuses only
 loop pairs that already satisfy the four legality conditions. It does
 not reshape the surrounding code to create new fusion opportunities,
 and several legality checks are intentionally conservative.
 
-7.1 Algorithmic Scope
-----------------------
+### 7.1 Algorithmic Scope
 
 - **No loop reshaping to enable fusion.** The pass does not insert
   guards, rotate loops, run loop-simplify, or otherwise modify loops
@@ -380,12 +354,11 @@ and several legality checks are intentionally conservative.
   consumed by the next loop are blocked even when the producing value
   is loop-invariant and could legally be hoisted.
 
-7.2 Trip-Count Equalization
-----------------------------
+### 7.2 Trip-Count Equalization
 
 - **Peeling is disabled by default.** The maximum number of
   iterations the pass may peel is controlled by the command-line
-  option ``-loop-fusion-peel-max-count``, which defaults to ``0``.
+  option `-loop-fusion-peel-max-count`, which defaults to `0`.
   Peeling therefore never fires unless the user opts in explicitly.
 - **Peeling shrinks only the first loop.** If the second loop has
   more iterations than the first, fusion is rejected. There is no
@@ -394,9 +367,7 @@ and several legality checks are intentionally conservative.
   loops must have a small constant trip count for the peel distance
   to be computed. Symbolic trip-count differences are not handled.
 
-
-7.4 Preheader and Block Handling
----------------------------------
+### 7.4 Preheader and Block Handling
 
 - **Volatile or atomic preheader instructions block fusion.** Such
   instructions cannot be moved, so a non-empty preheader that
@@ -416,14 +387,13 @@ and several legality checks are intentionally conservative.
   merged with their neighbors. The fused Control-Flow Graph (CFG)
   therefore retains more basic blocks than strictly necessary.
 
-7.5 Eligibility Blockers
--------------------------
+### 7.5 Eligibility Blockers
 
 A loop is rejected during candidate construction if any of the
 following properties holds:
 
 - A block in the loop has its address taken (for example, used as a
-  ``blockaddress`` operand).
+  `blockaddress` operand).
 - Any instruction in the loop may throw.
 - The loop contains a volatile memory access.
 - The loop contains an atomic memory access.
@@ -432,15 +402,15 @@ These conditions are hard blockers, not soft preferences: the pass
 never attempts to work around them.
 
 **Atomic accesses are rejected conservatively.** Any atomic
-instruction -- atomic ``load`` and ``store``, ``atomicrmw``,
-``cmpxchg``, and ``fence`` -- disqualifies the loop, mirroring the
+instruction -- atomic `load` and `store`, `atomicrmw`,
+`cmpxchg`, and `fence` -- disqualifies the loop, mirroring the
 volatile blocker. The underlying dependence analysis reasons about
 address-based data dependence rather than inter-thread
 synchronization (the synchronizes-with / fence semantics of the
 memory model), so fusing two loops interleaves their bodies and
 could reorder atomics in ways that change observable multi-threaded
 behavior even when the dependence check reports no conflict. The
-rejection covers the ``unordered`` ordering and higher, so even
-``unordered`` atomics (which carry no cross-thread ordering) are
-rejected. 
+rejection covers the `unordered` ordering and higher, so even
+`unordered` atomics (which carry no cross-thread ordering) are
+rejected.
 
diff --git a/llvm/docs/MLGO.md b/llvm/docs/MLGO.md
index 8f8a35d5e2693..00bcb66a6c633 100644
--- a/llvm/docs/MLGO.md
+++ b/llvm/docs/MLGO.md
@@ -1,210 +1,202 @@
-=============================================
-Machine Learning - Guided Optimization (MLGO)
-=============================================
+# Machine Learning - Guided Optimization (MLGO)
 
-Introduction
-============
+## Introduction
 
 MLGO refers to integrating ML techniques (primarily) to replace heuristics within
 LLVM with machine learned models.
 
 Currently the following heuristics feature such integration:
 
-* Inlining for size
-* Register allocation (LLVM greedy eviction heuristic) for performance
+- Inlining for size
+- Register allocation (LLVM greedy eviction heuristic) for performance
 
 This document is an outline of the tooling and APIs facilitating MLGO.
 
-.. note::
-    
-  The tools for orchestrating ML training are not part of LLVM, as they are
-  dependency-heavy - both on the ML infrastructure choice, as well as choices of
-  distributed computing. For the training scenario, LLVM only contains facilities
-  enabling it, such as corpus extraction, training data extraction, and evaluation
-  of models during training.
+:::{note}
+The tools for orchestrating ML training are not part of LLVM, as they are
+dependency-heavy - both on the ML infrastructure choice, as well as choices of
+distributed computing. For the training scenario, LLVM only contains facilities
+enabling it, such as corpus extraction, training data extraction, and evaluation
+of models during training.
+:::
 
+## Corpus Tooling
 
-
-Corpus Tooling
-==============
-
-Within the LLVM monorepo, there is the ``mlgo-utils`` Python package that
-lives at ``llvm/utils/mlgo-utils``. This package primarily contains tooling
+Within the LLVM monorepo, there is the `mlgo-utils` Python package that
+lives at `llvm/utils/mlgo-utils`. This package primarily contains tooling
 for working with corpora, or collections of LLVM bitcode. We use these corpora
 to train and evaluate ML models. Corpora consist of a description in JSON
-format at ``corpus_description.json`` in the root of the corpus, and then
+format at `corpus_description.json` in the root of the corpus, and then
 a bitcode file and command line flags file for each extracted module. The
 corpus structure is designed to contain sufficient information to fully
 compile the bitcode to bit-identical object files.
 
+```{eval-rst}
 .. program:: extract_ir.py
+```
 
-Synopsis
---------
+### Synopsis
 
 Extracts a corpus from some form of a structured compilation database. This
 tool supports a variety of different scenarios and input types.
 
-Options
--------
-
-.. option:: --input
-
-  The path to the input. This should be a path to a supported structured
-  compilation database. Currently only ``compile_commands.json`` files, linker
-  parameter files, a directory containing object files (for the local
-  ThinLTO case only), or a JSON file containing a bazel aquery result are
-  supported.
-
-.. option:: --input_type
-
-  The type of input that has been passed to the ``--input`` flag.
-
-.. option:: --output_dir
-
-  The output directory to place the corpus in.
-
-.. option:: --num_workers
-
-  The number of workers to use for extracting bitcode into the corpus. This
-  defaults to the number of hardware threads available on the host system.
-
-.. option:: --llvm_objcopy_path
-
-  The path to the llvm-objcopy binary to use when extracting bitcode.
-
-.. option:: --obj_base_dir
-
-  The base directory for object files. Bitcode files that get extracted into
-  the corpus will be placed into the output directory based on where their
-  source object files are placed relative to this path.
-
-.. option:: --cmd_filter
-
-  Allows filtering of modules by command line. If set, only modules that match
-  the filter will be extracted into the corpus. Regular expressions are
-  supported in some instances.
-
-.. option:: --thinlto_build
-
-  If the build was performed with ThinLTO, this should be set to either
-  ``distributed`` or ``local`` depending upon how the build was performed.
-
-.. option:: --cmd_section_name
-
-  This flag allows specifying the command line section name. This is needed
-  on non-ELF platforms where the section name might differ.
-
-.. option:: --bitcode_section_name
-
-  This flag allows specifying the bitcode section name. This is needed on
-  non-ELF platforms where the section name might differ.
-
-Example: CMake
---------------
-
-CMake can output a ``compilation_commands.json`` compilation database if the
-``CMAKE_EXPORT_COMPILE_COMMANDS`` switch is turned on at compile time. It is
-also necessary to enable bitcode embedding (done by passing 
-``-Xclang -fembed-bitcode=all`` to all C/C++ compilation actions in the
+### Options
+
+:::{option} --input
+The path to the input. This should be a path to a supported structured
+compilation database. Currently only `compile_commands.json` files, linker
+parameter files, a directory containing object files (for the local
+ThinLTO case only), or a JSON file containing a bazel aquery result are
+supported.
+:::
+
+:::{option} --input_type
+The type of input that has been passed to the `--input` flag.
+:::
+
+:::{option} --output_dir
+The output directory to place the corpus in.
+:::
+
+:::{option} --num_workers
+The number of workers to use for extracting bitcode into the corpus. This
+defaults to the number of hardware threads available on the host system.
+:::
+
+:::{option} --llvm_objcopy_path
+The path to the llvm-objcopy binary to use when extracting bitcode.
+:::
+
+:::{option} --obj_base_dir
+The base directory for object files. Bitcode files that get extracted into
+the corpus will be placed into the output directory based on where their
+source object files are placed relative to this path.
+:::
+
+:::{option} --cmd_filter
+Allows filtering of modules by command line. If set, only modules that match
+the filter will be extracted into the corpus. Regular expressions are
+supported in some instances.
+:::
+
+:::{option} --thinlto_build
+If the build was performed with ThinLTO, this should be set to either
+`distributed` or `local` depending upon how the build was performed.
+:::
+
+:::{option} --cmd_section_name
+This flag allows specifying the command line section name. This is needed
+on non-ELF platforms where the section name might differ.
+:::
+
+:::{option} --bitcode_section_name
+This flag allows specifying the bitcode section name. This is needed on
+non-ELF platforms where the section name might differ.
+:::
+
+### Example: CMake
+
+CMake can output a `compilation_commands.json` compilation database if the
+`CMAKE_EXPORT_COMPILE_COMMANDS` switch is turned on at compile time. It is
+also necessary to enable bitcode embedding (done by passing
+`-Xclang -fembed-bitcode=all` to all C/C++ compilation actions in the
 non-ThinLTO case). For example, to extract a corpus from clang, you would
 run the following commands (assuming that the system C/C++ compiler is clang):
 
-.. code-block:: bash
-
-  cmake -GNinja \
-    -DCMAKE_BUILD_TYPE=Release \
-    -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-    -DCMAKE_C_FLAGS="-Xclang -fembed-bitcode=all" \
-    -DCMAKE_CXX_FLAGS="-Xclang -fembed-bitcode-all"
-    ../llvm
-  ninja
+```bash
+cmake -GNinja \
+  -DCMAKE_BUILD_TYPE=Release \
+  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
+  -DCMAKE_C_FLAGS="-Xclang -fembed-bitcode=all" \
+  -DCMAKE_CXX_FLAGS="-Xclang -fembed-bitcode-all"
+  ../llvm
+ninja
+```
 
 After running CMake and building the project, there should be a
- ``compilation_commands.json`` file within the build directory. You can then
- run the following command to create a corpus:
 
-.. code-block:: bash
+: `compilation_commands.json` file within the build directory. You can then
+  run the following command to create a corpus:
 
-  python3 ./extract_ir.py \
-    --input=./build/compile_commands.json \
-    --input_type=json \
-    --output_dir=./corpus
+```bash
+python3 ./extract_ir.py \
+  --input=./build/compile_commands.json \
+  --input_type=json \
+  --output_dir=./corpus
+```
 
 After running the above command, there should be a full
-corpus of bitcode within the ``./corpus`` directory.
+corpus of bitcode within the `./corpus` directory.
 
-Example: Bazel Aquery
----------------------
+### Example: Bazel Aquery
 
 This tool also supports extracting bitcode from bazel in multiple ways
 depending upon the exact configuration. For ThinLTO, a linker parameters file
 is preferred. For the non-ThinLTO case, the script will accept the output of
-``bazel aquery`` which it will use to find all the object files that are linked
+`bazel aquery` which it will use to find all the object files that are linked
 into a specific target and then extract bitcode from them. First, you need
 to generate the aquery output:
 
-.. code-block:: bash
-
-  bazel aquery --output=jsonproto //path/to:target > /path/to/aquery.json
+```bash
+bazel aquery --output=jsonproto //path/to:target > /path/to/aquery.json
+```
 
 Afterwards, assuming that the build is already complete, you can run this
 script to create a corpus:
 
-.. code-block:: bash
-
-  python3 ./extract_ir.py \
-    --input=/path/to/aquery.json \
-    --input_type=bazel_aqeury \
-    --output_dir=./corpus \
-    --obj_base_dir=./bazel-bin
+```bash
+python3 ./extract_ir.py \
+  --input=/path/to/aquery.json \
+  --input_type=bazel_aqeury \
+  --output_dir=./corpus \
+  --obj_base_dir=./bazel-bin
+```
 
 This will again leave a corpus that contains all the bitcode files. This mode
 does not capture all object files in the build however, only the ones that
-are involved in the link for the binary passed to the ``bazel aquery``
+are involved in the link for the binary passed to the `bazel aquery`
 invocation.
 
+```{eval-rst}
 .. program:: make_corpus.py
+```
 
-Synopsis
---------
+### Synopsis
 
 Creates a corpus from a collection of bitcode files.
 
-Options
--------
-
-.. option:: --input_dir
+### Options
 
-  The input directory to search for bitcode files in.
+:::{option} --input_dir
+The input directory to search for bitcode files in.
+:::
 
-.. option:: --output_dir
+:::{option} --output_dir
+The output directory to place the constructed corpus in.
+:::
 
-  The output directory to place the constructed corpus in.
-
-.. option:: --default_args
-
-  A list of space separated flags that are put into the corpus description.
-  These are used by some tooling when compiling the modules within the corpus.
+:::{option} --default_args
+A list of space separated flags that are put into the corpus description.
+These are used by some tooling when compiling the modules within the corpus.
+:::
 
+```{eval-rst}
 .. program:: combine_training_corpus.py
+```
 
-Synopsis
---------
+### Synopsis
 
 Combines two training corpora that share the same parent folder by generating
-a new ``corpus_description.json`` that contains all the modules in both corpora.
-
-Options
--------
+a new `corpus_description.json` that contains all the modules in both corpora.
 
-.. option:: --root_dir
+### Options
 
-  The root directory that contains subfolders consisting of the corpora that
-  should be combined.
+:::{option} --root_dir
+The root directory that contains subfolders consisting of the corpora that
+should be combined.
+:::
 
-Interacting with ML models
-==========================
+## Interacting with ML models
 
 We interact with ML models in 2 primary scenarios: one is to train such a model.
 The other, inference, is to use a model during compilation, to make optimization
@@ -235,76 +227,71 @@ name, scalar type, and shape tuples.
 
 The main types in LLVM are:
 
-- ``MLModelRunner`` - an abstraction for the decision making mechanism
-- ``TensorSpec`` which describes a tensor.
+- `MLModelRunner` - an abstraction for the decision making mechanism
+- `TensorSpec` which describes a tensor.
 
-TensorSpec
-----------
+### TensorSpec
 
-See ``llvm/Analysis/TensorSpec.h``. This is a simple data bag, identifying a
+See `llvm/Analysis/TensorSpec.h`. This is a simple data bag, identifying a
 tensor by name (a string), scalar type, and shape (a vector of ints). The scalar
 type can only be int (8, 16, 32, or 64), signed or unsigned; float; or double.
 
-MLModelRunner
--------------
+### MLModelRunner
 
-See ``llvm/Analysis/MLModelRunner.h``. The abstraction has a pure virtual,
-``evaluateUntyped``, but the contract with implementers is a bit more involved:
+See `llvm/Analysis/MLModelRunner.h`. The abstraction has a pure virtual,
+`evaluateUntyped`, but the contract with implementers is a bit more involved:
 
-Implementers
-^^^^^^^^^^^^
+#### Implementers
 
-At construction, the implementer is expected to receive a list of ``TensorSpec``
-for input features and the ``TensorSpec`` of the output (e.g. 
-``std::vector<TensorSpec>``). The list type is not contractual, but it must be
-a 0-based indexing array-like container. Given a ``TensorSpec`` at index "I" in
+At construction, the implementer is expected to receive a list of `TensorSpec`
+for input features and the `TensorSpec` of the output (e.g.
+`std::vector<TensorSpec>`). The list type is not contractual, but it must be
+a 0-based indexing array-like container. Given a `TensorSpec` at index "I" in
 the input list, that has a name "N", shape "D1 x D2x ... Dn", and scalar type
 "T", the implementer must:
 
-- set up a contiguous buffer sized ``sizeof(T) * D1 * D2 * ... * Dn``. This
+- set up a contiguous buffer sized `sizeof(T) * D1 * D2 * ... * Dn`. This
   buffer's lifetime must be the same as the lifetime of the implementer object.
-- call ``MLModelRunner::setUpBufferForTensor`` passing I, the ``TensorSpec``,
+- call `MLModelRunner::setUpBufferForTensor` passing I, the `TensorSpec`,
   and the buffer above.
 
 Internally, the expectation is that the implementer uses the name (and maybe
-shape) of a ``TensorSpec`` for binding (e.g. lookup in an underlying ML model).
+shape) of a `TensorSpec` for binding (e.g. lookup in an underlying ML model).
 
-``MLModelRunner::setUpBufferForTensor`` stores each buffer at the corresponding
+`MLModelRunner::setUpBufferForTensor` stores each buffer at the corresponding
 index (i.e. its position in the list used at construction). The expectation is
-that the user will use that position when calling ``MLModelRunner::getTensor``
+that the user will use that position when calling `MLModelRunner::getTensor`
 to retrieve the underlying buffer (more on that in a bit).
 
-The implementation of ``evaluateUntyped`` is expected to use the value in the
+The implementation of `evaluateUntyped` is expected to use the value in the
 buffers described above, carry out whatever computation (e.g. evaluate a ML
 model) and then place the outcome in an output buffer which will be returned to
-the caller. Importantly, ``evaluateUntyped`` must not reset the input buffers.
+the caller. Importantly, `evaluateUntyped` must not reset the input buffers.
 This is because during training we may want to log the features and decisions,
 and since the data is already buffered, there's no reason to force backing it
 up elsewhere.
 
-Users
-^^^^^
+#### Users
 
-The users must pass the input ``TensorSpec`` list at the construction of a
-specific ``MLModelRunner`` object. After that, users can be agnostic of the
+The users must pass the input `TensorSpec` list at the construction of a
+specific `MLModelRunner` object. After that, users can be agnostic of the
 specific implementation, and would typically follow the following workflow:
 
-- call ``getTensor`` or ``getTensorUntyped``, for each input tensor, identified
-  by its index (i.e. the index of the corresponding ``TensorSpec`` in the list
+- call `getTensor` or `getTensorUntyped`, for each input tensor, identified
+  by its index (i.e. the index of the corresponding `TensorSpec` in the list
   used at construction).
 - populate the tensor buffer of each input tensor with values. Users can take
   advantage of the stability of the tensor buffers like set only once those that
   don't change, or cache the buffer address
-- call ``evaluate`` and use its result.
+- call `evaluate` and use its result.
 
-Versioning
-^^^^^^^^^^
+#### Versioning
 
 We support a model "knowing" less inputs than the compiler. This is supported by
-``MLModelRunner::setUpBufferForTensor``. If a ``TensorSpec`` requested by the
-compiler is not supported by the underlying model, the ``MLModelRunner``
-implementer must still call ``setUpBufferForTensor`` with a ``nullptr`` value
-for the buffer. In turn, ``MLModelRunner`` will allocate an appropriately - sized
+`MLModelRunner::setUpBufferForTensor`. If a `TensorSpec` requested by the
+compiler is not supported by the underlying model, the `MLModelRunner`
+implementer must still call `setUpBufferForTensor` with a `nullptr` value
+for the buffer. In turn, `MLModelRunner` will allocate an appropriately - sized
 buffer and track its lifetime. The user can safely populate that buffer. Since
 the rest of the inputs are still provided, this allows an evolution model where
 we first add features to the compiler and continue using older models without
@@ -312,37 +299,34 @@ regressing. Then, the new compiler can be used to train new models. Deprecating
 features in the compiler involves, then, training first a model without those
 features.
 
-``MLModelRunner`` implementations
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+#### `MLModelRunner` implementations
 
 We currently feature 4 implementations:
 
-- ``ModelUnderTrainingRunner``. This requires the compiler be built with TFLite
+- `ModelUnderTrainingRunner`. This requires the compiler be built with TFLite
   support. It allows loading a TFLite model dynamically and is primarily
   intended for training scenarios, but it can be used relatively easily in
   production build environments, as it does not change how the compiler operates
   (why this remark is necessary will become clear in a few paragraphs)
-
-- ``ReleaseModeModelRunner``. This is intended for inference scenarios. This
-  uses the rules defined in ``llvm/cmake/modules/TensorFlowCompile.cmake`` to
+- `ReleaseModeModelRunner`. This is intended for inference scenarios. This
+  uses the rules defined in `llvm/cmake/modules/TensorFlowCompile.cmake` to
   convert, at the time the compiler is built, TensorFlow Saved Models into a
   header (.h) and native object (.o). The latter is a CPU-based implementation of
   the neural network, together with its weights (essentially, loops performing
   matrix multiplications)
 
-.. note::
-    
-  we are actively working on replacing this with an EmitC implementation
-  requiring no out of tree build-time dependencies.
+:::{note}
+we are actively working on replacing this with an EmitC implementation
+requiring no out of tree build-time dependencies.
+:::
 
-- ``InteractiveModelRunner``. This is intended for training scenarios where the
+- `InteractiveModelRunner`. This is intended for training scenarios where the
   training algorithm drives compilation. This model runner has no special
   dependencies, and relies on I/O pipes to communicate with a separate process,
   presumably a python training algorithm. We do not envision using this in a
   production environment.
-
-- ``NoInferenceModelRunner``. This serves as a store for feature values, and its
-  ``evaluate`` should never be called. It's used for training scenarios, when we
+- `NoInferenceModelRunner`. This serves as a store for feature values, and its
+  `evaluate` should never be called. It's used for training scenarios, when we
   want to capture the behavior of the default (non-ML) heuristic.
 
 Note that training leaves it to the training infrastructure to handle
@@ -350,8 +334,7 @@ distributed computing. The assumed architecture has python processes
 communicating remotely between themselves, but managing local communication with
 clang.
 
-Logging Facility
-----------------
+### Logging Facility
 
 When training models, we need to expose the features we will want to use during
 inference, as well as outcomes, to guide reward-based learning techniques. This
@@ -359,22 +342,21 @@ can happen in 2 forms:
 
 - when running the compiler on some input, as a capture of the features and
   actions taken by some policy or a model currently being used.
-  For example, see ``DevelopmentModeInlineAdvisor`` or ``DevelopmentModeEvictAdvisor``
-  in ``MLRegallocEvictAdvisor.cpp``. In more detail, in the former case, if
-  ``-training-log`` is specified, the features and actions (inline/no inline)
+  For example, see `DevelopmentModeInlineAdvisor` or `DevelopmentModeEvictAdvisor`
+  in `MLRegallocEvictAdvisor.cpp`. In more detail, in the former case, if
+  `-training-log` is specified, the features and actions (inline/no inline)
   from each inlining decision are saved to the specified file. Since
-  ``MLModelRunner`` implementations hold on to feature values (they don't get
-  cleared by ``evaluate``), logging is easily supported by just looping over the
+  `MLModelRunner` implementations hold on to feature values (they don't get
+  cleared by `evaluate`), logging is easily supported by just looping over the
   model runner's features and passing the tensor buffers to the logger. Note how
-  we use the ``NoInferenceModelRunner`` to capture the features observed when
+  we use the `NoInferenceModelRunner` to capture the features observed when
   using the default policy.
-
-- as a serialization mechanism for the ``InteractiveModelRunner``. Here, we need
+- as a serialization mechanism for the `InteractiveModelRunner`. Here, we need
   to pass the observed features over IPC (a file descriptor, likely a named
   pipe).
 
 Both cases require serializing the same kind of data and we support both with
-``Analysis/Utils/TrainingLogger``.
+`Analysis/Utils/TrainingLogger`.
 
 The goal of the logger design was avoiding any new dependency, and optimizing
 for the tensor scenario - i.e. exchanging potentially large buffers of fixed
@@ -388,34 +370,38 @@ The logger produces the following sequence:
 
 - a header describing the structure of the log. This is a one-line textual JSON
   dictionary with the following elements:
-  
-  - ``features``: a list of JSON-serialized ``TensorSpec`` values. The position
+
+  - `features`: a list of JSON-serialized `TensorSpec` values. The position
     in the list matters, as it will be the order in which values will be
     subsequently recorded. If we are just logging (i.e. not using the
-    ``InteractiveModelRunner``), the last feature should be that of the action
+    `InteractiveModelRunner`), the last feature should be that of the action
     (e.g. "inline/no inline", or "index of evicted live range")
-  - (optional) ``score``: a ``TensorSpec`` describing a value we will include to
+  - (optional) `score`: a `TensorSpec` describing a value we will include to
     help formulate a reward. This could be a size estimate or a latency estimate.
-  - (optional) ``advice``: a ``TensorSpec`` describing the action. This is used
-    for the ``InteractiveModelRunner``, in which case it shouldn't be in the 
-    ``features`` list.
-- a sequence of ``contexts``. Contexts are independent traces of the optimization
+  - (optional) `advice`: a `TensorSpec` describing the action. This is used
+    for the `InteractiveModelRunner`, in which case it shouldn't be in the
+    `features` list.
+
+- a sequence of `contexts`. Contexts are independent traces of the optimization
   problem. For module passes, there is only one context, for function passes,
   there is a context per function. The start of a context is marked with a
-  one-line JSON dictionary of the form ``{"context": <context name, a string>}``
-  
+  one-line JSON dictionary of the form `{"context": <context name, a string>}`
+
   Each context has a sequence of:
 
-  - ``observations``. An observation is:
-    
-    - one-line JSON ``{"observation": <observation number. 0-indexed>}``
+  - `observations`. An observation is:
+
+    - one-line JSON `{"observation": <observation number. 0-indexed>}`
+
     - a binary dump of the tensor buffers, in the order in which they were
       specified in the header.
+
     - a new line character
-    - if ``score`` was specified in the header:
-    
-      - a one-line JSON object ``{"outcome": <value>}``, where the ``value``
-        conforms to the ``TensorSpec`` in defined for the ``score`` in the header.
+
+    - if `score` was specified in the header:
+
+      - a one-line JSON object `{"outcome": <value>}`, where the `value`
+        conforms to the `TensorSpec` in defined for the `score` in the header.
       - the outcome value, as a binary dump
       - a new line character.
 
@@ -426,15 +412,14 @@ additional dependencies; and the one-line JSON makes it rudimentarily possible
 to inspect a log without additional tooling.
 
 A python utility for reading logs, used for tests, is available at
-``Analysis/models/log_reader.py``. A utility showcasing the ``InteractiveModelRunner``,
-which uses this reader as well, is at ``Analysis/models/interactive_host.py``.
+`Analysis/models/log_reader.py`. A utility showcasing the `InteractiveModelRunner`,
+which uses this reader as well, is at `Analysis/models/interactive_host.py`.
 The latter is also used in tests.
 
 There is no C++ implementation of a log reader. We do not have a scenario
 motivating one.
 
-Embeddings
-==========
+## Embeddings
 
 LLVM provides embedding frameworks to generate vector representations of code
 at different abstraction levels. These embeddings capture syntactic, semantic,
@@ -452,160 +437,151 @@ point vectors. These embeddings can be computed at multiple granularity levels
 (instruction, basic block, and function) and used for ML-guided compiler
 optimizations.
 
-IR2Vec
-------
+### IR2Vec
 
 IR2Vec is a program embedding approach designed specifically for LLVM IR. It
 is implemented as a function analysis pass in LLVM. The IR2Vec embeddings
-capture syntactic, semantic, and structural properties of the IR through 
-learned representations. These representations are obtained as a JSON 
-vocabulary that maps the entities of the IR (opcodes, types, operands) to 
-n-dimensional floating point vectors (embeddings). 
+capture syntactic, semantic, and structural properties of the IR through
+learned representations. These representations are obtained as a JSON
+vocabulary that maps the entities of the IR (opcodes, types, operands) to
+n-dimensional floating point vectors (embeddings).
 
 With IR2Vec, representation at different granularities of IR, such as
-instructions, functions, and basic blocks, can be obtained. Representations 
+instructions, functions, and basic blocks, can be obtained. Representations
 of loops and regions can be derived from these representations, which can be
 useful in different scenarios. The representations can be useful for various
 downstream tasks, including ML-guided compiler optimizations.
 
 The core components are:
-  - **Vocabulary**: A mapping from IR entities (opcodes, types, etc.) to their
-    vector representations. This is managed by ``IR2VecVocabAnalysis``. The 
-    vocabulary (.json file) contains three sections -- Opcodes, Types, and 
-    Arguments, each containing the representations of the corresponding 
+: - **Vocabulary**: A mapping from IR entities (opcodes, types, etc.) to their
+    vector representations. This is managed by `IR2VecVocabAnalysis`. The
+    vocabulary (.json file) contains three sections -- Opcodes, Types, and
+    Arguments, each containing the representations of the corresponding
     entities.
 
-    .. note::
-      
-      It is mandatory to have these three sections present in the vocabulary file 
-      for it to be valid; order in which they appear does not matter.
+    :::{note}
+    It is mandatory to have these three sections present in the vocabulary file
+    for it to be valid; order in which they appear does not matter.
+    :::
 
-  - **Embedder**: A class (``ir2vec::Embedder``) that uses the vocabulary to
+  - **Embedder**: A class (`ir2vec::Embedder`) that uses the vocabulary to
     compute embeddings for instructions, basic blocks, and functions.
 
-Using IR2Vec
-^^^^^^^^^^^^
-
-.. note::
+#### Using IR2Vec
 
-   This section describes how to use IR2Vec within LLVM passes. A standalone 
-   tool :doc:`CommandGuide/llvm-ir2vec` is available for generating the
-   embeddings and triplets from LLVM IR files, which can be useful for
-   training vocabularies and generating embeddings outside of compiler passes.
+:::{note}
+This section describes how to use IR2Vec within LLVM passes. A standalone
+tool {doc}`CommandGuide/llvm-ir2vec` is available for generating the
+embeddings and triplets from LLVM IR files, which can be useful for
+training vocabularies and generating embeddings outside of compiler passes.
+:::
 
-For generating embeddings, first the vocabulary should be obtained. Then, the 
-embeddings can be computed and accessed via an ``ir2vec::Embedder`` instance.
+For generating embeddings, first the vocabulary should be obtained. Then, the
+embeddings can be computed and accessed via an `ir2vec::Embedder` instance.
 
 1. **Get the Vocabulary**:
    In a ModulePass, get the vocabulary analysis result:
 
-   .. code-block:: c++
+   ```c++
+   auto &VocabRes = MAM.getResult<IR2VecVocabAnalysis>(M);
+   if (!VocabRes.isValid()) {
+     // Handle error: vocabulary is not available or invalid
+     return;
+   }
+   const ir2vec::Vocab &Vocabulary = VocabRes.getVocabulary();
+   ```
 
-      auto &VocabRes = MAM.getResult<IR2VecVocabAnalysis>(M);
-      if (!VocabRes.isValid()) {
-        // Handle error: vocabulary is not available or invalid
-        return;
-      }
-      const ir2vec::Vocab &Vocabulary = VocabRes.getVocabulary();
-
-   Note that ``IR2VecVocabAnalysis`` pass is immutable.
+   Note that `IR2VecVocabAnalysis` pass is immutable.
 
 2. **Create Embedder instance**:
    With the vocabulary, create an embedder for a specific function:
 
-   .. code-block:: c++
-
-      // Assuming F is an llvm::Function&
-      // For example, using IR2VecKind::Symbolic:
-      std::unique_ptr<ir2vec::Embedder> Emb =
-          ir2vec::Embedder::create(IR2VecKind::Symbolic, F, Vocabulary);
-
+   ```c++
+   // Assuming F is an llvm::Function&
+   // For example, using IR2VecKind::Symbolic:
+   std::unique_ptr<ir2vec::Embedder> Emb =
+       ir2vec::Embedder::create(IR2VecKind::Symbolic, F, Vocabulary);
+   ```
 
 3. **Compute and Access Embeddings**:
-   Call ``getFunctionVector()`` to get the embedding for the function. 
+   Call `getFunctionVector()` to get the embedding for the function.
 
-   .. code-block:: c++
+   ```c++
+   ir2vec::Embedding FuncVector = Emb->getFunctionVector();
+   ```
 
-    ir2vec::Embedding FuncVector = Emb->getFunctionVector();
-
-   Currently, ``Embedder`` can generate embeddings at three levels: Instructions,
+   Currently, `Embedder` can generate embeddings at three levels: Instructions,
    Basic Blocks, and Functions. Appropriate getters are provided to access the
    embeddings at these levels.
 
-   .. note::
-
-    The validity of ``Embedder`` instance (and the embeddings it generates) is
-    tied to the function it is associated with remains unchanged. If the function
-    is modified, the embeddings may become stale and should be recomputed accordingly.
+   :::{note}
+   The validity of `Embedder` instance (and the embeddings it generates) is
+   tied to the function it is associated with remains unchanged. If the function
+   is modified, the embeddings may become stale and should be recomputed accordingly.
+   :::
 
 4. **Working with Embeddings:**
-   Embeddings are represented as ``std::vector<double>``. These
+   Embeddings are represented as `std::vector<double>`. These
    vectors as features for machine learning models, compute similarity scores
    between different code snippets, or perform other analyses as needed.
 
-Further Details
-^^^^^^^^^^^^^^^
+#### Further Details
 
 For more detailed information about the IR2Vec algorithm, its parameters, and
 advanced usage, please refer to the original paper:
-`IR2Vec: LLVM IR Based Scalable Program Embeddings <https://doi.org/10.1145/3418463>`_.
+[IR2Vec: LLVM IR Based Scalable Program Embeddings](https://doi.org/10.1145/3418463).
 
 For information about using IR2Vec tool for generating embeddings and
-triplets from LLVM IR, see :doc:`CommandGuide/llvm-ir2vec`.
+triplets from LLVM IR, see {doc}`CommandGuide/llvm-ir2vec`.
 
-The LLVM source code for ``IR2Vec`` can also be explored to understand the 
+The LLVM source code for `IR2Vec` can also be explored to understand the
 implementation details.
 
-MIR2Vec
--------
+### MIR2Vec
 
-MIR2Vec is an extension of IR2Vec designed specifically for LLVM Machine IR 
-(MIR). It generates embeddings for machine-level instructions, basic blocks, 
+MIR2Vec is an extension of IR2Vec designed specifically for LLVM Machine IR
+(MIR). It generates embeddings for machine-level instructions, basic blocks,
 and functions. MIR2Vec operates on the target-specific machine representation,
-capturing machine instruction semantics including opcodes, operands, and 
+capturing machine instruction semantics including opcodes, operands, and
 register information at the machine level.
 
 MIR2Vec extends the vocabulary to include:
 
 - **Machine Opcodes**: Target-specific instruction opcodes derived from the
   TargetInstrInfo, grouped by instruction semantics.
-
 - **Common Operands**: All common operand types (excluding register operands),
-  defined by the ``MachineOperand::MachineOperandType`` enum.
-
+  defined by the `MachineOperand::MachineOperandType` enum.
 - **Physical Register Classes**: Register classes defined by the target,
   specialized for physical registers.
-
 - **Virtual Register Classes**: Register classes defined by the target,
   specialized for virtual registers.
 
 The core components are:
 
 - **Vocabulary**: A mapping from machine IR entities (opcodes, operands, register
-  classes) to their vector representations. This is managed by 
-  ``MIR2VecVocabLegacyAnalysis`` for the legacy pass manager, with a 
-  ``MIR2VecVocabProvider`` that can be used standalone or wrapped by pass 
-  managers. The vocabulary (.json file) contains sections for opcodes, common 
+  classes) to their vector representations. This is managed by
+  `MIR2VecVocabLegacyAnalysis` for the legacy pass manager, with a
+  `MIR2VecVocabProvider` that can be used standalone or wrapped by pass
+  managers. The vocabulary (.json file) contains sections for opcodes, common
   operands, physical register classes, and virtual register classes.
 
-  .. note::
-    
-    The vocabulary file should contain these sections for it to be valid.
+  :::{note}
+  The vocabulary file should contain these sections for it to be valid.
+  :::
 
-- **Embedder**: A class (``mir2vec::MIREmbedder``) that uses the vocabulary to
-  compute embeddings for machine instructions, machine basic blocks, and 
-  machine functions. Currently, ``SymbolicMIREmbedder`` is the available 
+- **Embedder**: A class (`mir2vec::MIREmbedder`) that uses the vocabulary to
+  compute embeddings for machine instructions, machine basic blocks, and
+  machine functions. Currently, `SymbolicMIREmbedder` is the available
   implementation.
 
-Using MIR2Vec
-^^^^^^^^^^^^^
-
-.. note::
+#### Using MIR2Vec
 
-   This section describes how to use MIR2Vec within LLVM passes. `llvm-ir2vec`
-   tool ` :doc:`CommandGuide/llvm-ir2vec` can be used for generating MIR2Vec
-   embeddings from Machine IR files (.mir), which can be useful for generating
-   embeddings outside of compiler passes.
+:::{note}
+This section describes how to use MIR2Vec within LLVM passes. `llvm-ir2vec`
+tool \` {doc}`CommandGuide/llvm-ir2vec` can be used for generating MIR2Vec
+embeddings from Machine IR files (.mir), which can be useful for generating
+embeddings outside of compiler passes.
+:::
 
 To generate MIR2Vec embeddings in a compiler pass, first obtain the vocabulary,
 then create an embedder instance to compute and access embeddings.
@@ -613,131 +589,126 @@ then create an embedder instance to compute and access embeddings.
 1. **Get the Vocabulary**:
    In a MachineFunctionPass, get the vocabulary from the analysis:
 
-   .. code-block:: c++
+   ```c++
+   auto &VocabAnalysis = getAnalysis<MIR2VecVocabLegacyAnalysis>();
+   auto VocabOrErr = VocabAnalysis.getMIR2VecVocabulary(*MF.getFunction().getParent());
+   if (!VocabOrErr) {
+     // Handle error: vocabulary is not available or invalid
+     return;
+   }
+   const mir2vec::MIRVocabulary &Vocabulary = *VocabOrErr;
+   ```
 
-      auto &VocabAnalysis = getAnalysis<MIR2VecVocabLegacyAnalysis>();
-      auto VocabOrErr = VocabAnalysis.getMIR2VecVocabulary(*MF.getFunction().getParent());
-      if (!VocabOrErr) {
-        // Handle error: vocabulary is not available or invalid
-        return;
-      }
-      const mir2vec::MIRVocabulary &Vocabulary = *VocabOrErr;
-
-   Note that ``MIR2VecVocabLegacyAnalysis`` is an immutable pass.
+   Note that `MIR2VecVocabLegacyAnalysis` is an immutable pass.
 
 2. **Create Embedder instance**:
    With the vocabulary, create an embedder for a specific machine function:
 
-   .. code-block:: c++
-
-      // Assuming MF is a MachineFunction&
-      // For example, using MIR2VecKind::Symbolic:
-      std::unique_ptr<mir2vec::MIREmbedder> Emb =
-          mir2vec::MIREmbedder::create(MIR2VecKind::Symbolic, MF, Vocabulary);
-
+   ```c++
+   // Assuming MF is a MachineFunction&
+   // For example, using MIR2VecKind::Symbolic:
+   std::unique_ptr<mir2vec::MIREmbedder> Emb =
+       mir2vec::MIREmbedder::create(MIR2VecKind::Symbolic, MF, Vocabulary);
+   ```
 
 3. **Compute and Access Embeddings**:
-   Call ``getMFunctionVector()`` to get the embedding for the machine function.
-
-   .. code-block:: c++
+   Call `getMFunctionVector()` to get the embedding for the machine function.
 
-    mir2vec::Embedding FuncVector = Emb->getMFunctionVector();
+   ```c++
+   mir2vec::Embedding FuncVector = Emb->getMFunctionVector();
+   ```
 
-   Currently, ``MIREmbedder`` can generate embeddings at three levels: Machine
-   Instructions, Machine Basic Blocks, and Machine Functions. Appropriate 
+   Currently, `MIREmbedder` can generate embeddings at three levels: Machine
+   Instructions, Machine Basic Blocks, and Machine Functions. Appropriate
    getters are provided to access the embeddings at these levels.
 
-   .. note::
-
-    The validity of the ``MIREmbedder`` instance (and the embeddings it 
-    generates) is tied to the machine function it is associated with. If the 
-    machine function is modified, the embeddings may become stale and should 
-    be recomputed accordingly.
+   :::{note}
+   The validity of the `MIREmbedder` instance (and the embeddings it
+   generates) is tied to the machine function it is associated with. If the
+   machine function is modified, the embeddings may become stale and should
+   be recomputed accordingly.
+   :::
 
 4. **Working with Embeddings:**
-   Embeddings are represented as ``std::vector<double>``. These vectors can be
+   Embeddings are represented as `std::vector<double>`. These vectors can be
    used as features for machine learning models, compute similarity scores
    between different code snippets, or perform other analyses as needed.
 
-Further Details
-^^^^^^^^^^^^^^^
+#### Further Details
 
 For more detailed information about the MIR2Vec algorithm, its parameters, and
 advanced usage, please refer to the original paper:
-`RL4ReAl: Reinforcement Learning for Register Allocation <https://doi.org/10.1145/3578360.3580273>`_.
+[RL4ReAl: Reinforcement Learning for Register Allocation](https://doi.org/10.1145/3578360.3580273).
 
 For information about using MIR2Vec tool for generating embeddings from
-Machine IR, see :doc:`CommandGuide/llvm-ir2vec`.
+Machine IR, see {doc}`CommandGuide/llvm-ir2vec`.
 
-The LLVM source code for ``MIR2Vec`` can be explored to understand the 
-implementation details. See ``llvm/include/llvm/CodeGen/MIR2Vec.h`` and 
-``llvm/lib/CodeGen/MIR2Vec.cpp``.
+The LLVM source code for `MIR2Vec` can be explored to understand the
+implementation details. See `llvm/include/llvm/CodeGen/MIR2Vec.h` and
+`llvm/lib/CodeGen/MIR2Vec.cpp`.
 
-Building with ML support
-========================
+## Building with ML support
 
-.. note::
-  
-  For up to date information on custom builds, see the ``ml-*``
-  `build bots <http://lab.llvm.org>`_. They are set up using 
-  `like this <https://github.com/google/ml-compiler-opt/blob/main/buildbot/buildbot_init.sh>`_.
+:::{note}
+For up to date information on custom builds, see the `ml-*`
+[build bots](http://lab.llvm.org). They are set up using
+[like this](https://github.com/google/ml-compiler-opt/blob/main/buildbot/buildbot_init.sh).
+:::
 
-Embed pre-trained models (aka "release" mode)
----------------------------------------------
+### Embed pre-trained models (aka "release" mode)
 
-This supports the ``ReleaseModeModelRunner`` model runners.
+This supports the `ReleaseModeModelRunner` model runners.
 
 You need a tensorflow pip package for the AOT (ahead-of-time) Saved Model compiler
 and a thin wrapper for the native function generated by it. We currently support
 TF 2.15. We recommend using a Python virtual env (in which case, remember to
-pass ``-DPython3_ROOT_DIR`` to ``cmake``).
+pass `-DPython3_ROOT_DIR` to `cmake`).
 
 Once you install the pip package, find where it was installed:
 
-.. code-block:: console
-
-  TF_PIP=$(sudo -u buildbot python3 -c "import tensorflow as tf; import os; print(os.path.dirname(tf.__file__))")``
+```console
+TF_PIP=$(sudo -u buildbot python3 -c "import tensorflow as tf; import os; print(os.path.dirname(tf.__file__))")``
+```
 
 Then build LLVM:
 
-.. code-block:: console
-
-  cmake -DTENSORFLOW_AOT_PATH=$TF_PIP \
-    -DLLVM_INLINER_MODEL_PATH=<path to inliner saved model dir> \
-    -DLLVM_RAEVICT_MODEL_PATH=<path to regalloc eviction saved model dir> \
-    <...other options...> 
+```console
+cmake -DTENSORFLOW_AOT_PATH=$TF_PIP \
+  -DLLVM_INLINER_MODEL_PATH=<path to inliner saved model dir> \
+  -DLLVM_RAEVICT_MODEL_PATH=<path to regalloc eviction saved model dir> \
+  <...other options...>
+```
 
 The example shows the flags for both inlining and regalloc, but either may be
 omitted.
 
 You can also specify a URL for the path, and it is also possible to pre-compile
 the header and object and then just point to the precompiled artifacts. See for
-example ``LLVM_OVERRIDE_MODEL_HEADER_INLINERSIZEMODEL``.
-
-.. note::
+example `LLVM_OVERRIDE_MODEL_HEADER_INLINERSIZEMODEL`.
 
-  We are transitioning away from the AOT compiler shipping with the
-  tensorflow package, and to a EmitC, in-tree solution, so these details will
-  change soon.
+:::{note}
+We are transitioning away from the AOT compiler shipping with the
+tensorflow package, and to a EmitC, in-tree solution, so these details will
+change soon.
+:::
 
-Using TFLite (aka "development" mode)
--------------------------------------
+### Using TFLite (aka "development" mode)
 
-This supports the ``ModelUnderTrainingRunner`` model runners.
+This supports the `ModelUnderTrainingRunner` model runners.
 
-Build the TFLite package using `this script <https://raw.githubusercontent.com/google/ml-compiler-opt/refs/heads/main/buildbot/build_tflite.sh>`_.
-Then, assuming you ran that script in ``/tmp/tflitebuild``, just pass
-``-C /tmp/tflitebuild/tflite.cmake`` to the ``cmake`` for LLVM.
+Build the TFLite package using [this script](https://raw.githubusercontent.com/google/ml-compiler-opt/refs/heads/main/buildbot/build_tflite.sh).
+Then, assuming you ran that script in `/tmp/tflitebuild`, just pass
+`-C /tmp/tflitebuild/tflite.cmake` to the `cmake` for LLVM.
 
-Interactive Mode (for training / research)
------------------------------------------- 
+### Interactive Mode (for training / research)
 
-The ``InteractiveModelRunner`` is available with no extra dependencies. For the
+The `InteractiveModelRunner` is available with no extra dependencies. For the
 optimizations that are currently MLGO-enabled, it may be used as follows:
 
-- for inlining: ``-mllvm -enable-ml-inliner=release -mllvm -inliner-interactive-channel-base=<name>``
-- for regalloc eviction: ``-mllvm -regalloc-evict-advisor=release -mllvm -regalloc-evict-interactive-channel-base=<name>``
+- for inlining: `-mllvm -enable-ml-inliner=release -mllvm -inliner-interactive-channel-base=<name>`
+- for regalloc eviction: `-mllvm -regalloc-evict-advisor=release -mllvm -regalloc-evict-interactive-channel-base=<name>`
+
+where the `name` is a path fragment. We will expect to find 2 files,
+`<name>.in` (readable, data incoming from the managing process) and
+`<name>.out` (writable, the model runner sends data to the managing process)
 
-where the ``name`` is a path fragment. We will expect to find 2 files,
-``<name>.in`` (readable, data incoming from the managing process) and
-``<name>.out`` (writable, the model runner sends data to the managing process)

>From 64b9d8d2b82db3082ab75ddd1afc5f44c6273b11 Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Fri, 11 Sep 2026 16:10:35 +0000
Subject: [PATCH 2/3] [LLVM][docs] Finish MyST migration for remaining docs
 (batch 10)

---
 llvm/docs/BranchWeightMetadata.md     |   7 +-
 llvm/docs/BugLifeCycle.md             |  68 ++++++++-------
 llvm/docs/CIBestPractices.md          |   1 -
 llvm/docs/CodeReview.md               |   7 +-
 llvm/docs/ConvergenceAndUniformity.md |  66 +++++++-------
 llvm/docs/ConvergentOperations.md     | 121 +++++++++++++-------------
 llvm/docs/DependenceGraphs/index.md   |  53 ++++++-----
 llvm/docs/FaultMaps.md                |  18 ++--
 llvm/docs/FuzzingLLVM.md              |  44 +++++-----
 llvm/docs/GetElementPtr.md            |  15 ++--
 llvm/docs/GitHubActionsRunners.md     |   5 --
 llvm/docs/GwpAsan.md                  |   7 +-
 llvm/docs/HowToAddABuilder.md         |  87 +++++++++---------
 llvm/docs/HowToReleaseLLVM.md         |  13 +--
 llvm/docs/HowToSetUpLLVMStyleRTTI.md  |   3 +-
 llvm/docs/HowToSubmitABug.md          |  18 ++--
 llvm/docs/HowToUseInstrMappings.md    |   3 +-
 llvm/docs/MLGO.md                     |  23 +++--
 18 files changed, 278 insertions(+), 281 deletions(-)

diff --git a/llvm/docs/BranchWeightMetadata.md b/llvm/docs/BranchWeightMetadata.md
index 06d8412105c9b..33652bb71c767 100644
--- a/llvm/docs/BranchWeightMetadata.md
+++ b/llvm/docs/BranchWeightMetadata.md
@@ -9,7 +9,7 @@ The first operand is always an `MDString` node with the string
 "branch_weights". The number of operands depends on the terminator type.
 
 Branch weights might be fetched from the profiling file or generated based on
-[\_\_builtin_expect][__builtin_expect] and [\_\_builtin_expect_with_probability][__builtin_expect_with_probability] instructions.
+{ref}`__builtin_expect <__builtin_expect>` and {ref}`__builtin_expect_with_probability <__builtin_expect_with_probability>` instructions.
 
 All weights are represented as unsigned 32-bit values, where a higher value
 indicates a greater chance of being taken.
@@ -97,7 +97,7 @@ is used.
 
 Other terminator instructions are not allowed to contain Branch Weight Metadata.
 
-(builtin-expect)=
+(__builtin_expect)=
 
 ## Built-in `expect` Instructions
 
@@ -134,7 +134,7 @@ case 5:  // This case is likely to be taken.
 }
 ```
 
-(builtin-expect-with-probability)=
+(__builtin_expect_with_probability)=
 
 ## Built-in `expect.with.probability` Instruction
 
@@ -209,4 +209,3 @@ annotation. The reason why we cannot annotate this on the callsite is that it
 can only go down 1 level in the call chain. For the cases where
 `foo_in_a_cc()->bar_in_b_cc()->baz_in_c_cc()`, we will need to go down 2 levels
 in the call chain to import both `bar_in_b_cc` and `baz_in_c_cc`.
-
diff --git a/llvm/docs/BugLifeCycle.md b/llvm/docs/BugLifeCycle.md
index e3cb8eea9cab3..041b46fdde4b7 100644
--- a/llvm/docs/BugLifeCycle.md
+++ b/llvm/docs/BugLifeCycle.md
@@ -15,17 +15,17 @@ reports.
 
 The main parts of the life cycle documented here are:
 
-1. [Reporting]
-2. [Triaging]
-3. [Actively working on fixing]
-4. [Closing]
+1. {ref}`Reporting <Reporting>`
+2. {ref}`Triaging <Triaging>`
+3. {ref}`Actively working on fixing <Actively working on fixing>`
+4. {ref}`Closing <Closing>`
 
 Furthermore, some of the metadata in the bug tracker, such as what labels we
 use, needs to be maintained. See the following for details:
 
-1. [Maintenance of metadata]
+1. {ref}`Maintenance of metadata <Maintenance of metadata>`
 
-(reporting)=
+(Reporting)=
 
 ## Reporting bugs
 
@@ -35,7 +35,7 @@ You can apply [labels](https://docs.github.com/en/issues/using-labels-and-milest
 to the bug to provide extra information to make the bug easier to discover, such
 as a label for the part of the project the bug pertains to.
 
-(triaging)=
+(Triaging)=
 
 ## Triaging bugs
 
@@ -81,7 +81,7 @@ good, actionable state. Try to answer the following questions while triaging:
 - If you are unsure of what a label is intended to be used for, please see the
   [documentation for our labels](https://github.com/llvm/llvm-project/labels).
 
-(actively-working-on-fixing)=
+(Actively working on fixing)=
 
 ## Actively working on fixing bugs
 
@@ -89,35 +89,40 @@ Please remember to assign the bug to yourself if you're actively working on
 fixing it and to unassign it when you're no longer actively working on it. You
 unassign a bug by removing the person from the `Assignees` field.
 
-(closing)=
+(Closing)=
+(resolving-closing-bugs)=
 
 ## Resolving/Closing bugs
 
 Resolving bugs is good! Make sure to properly record the reason for resolving.
 Examples of reasons for resolving are:
 
-> - If the issue has been resolved by a particular commit, close the issue with
->   a brief comment mentioning which commit(s) fixed it. If you are authoring
->   the fix yourself, your git commit message may include the phrase
->   `Fixes #<issue number>` on a line by itself. GitHub recognizes such commit
->   messages and will automatically close the specified issue with a reference
->   to your commit.
-> - If the reported behavior is not a bug, it is appropriate to close the issue
->   with a comment explaining why you believe it is not a bug, and adding the
->   `invalid` tag.
-> - If the bug duplicates another issue, close it as a duplicate by adding the
->   `duplicate` label with a comment pointing to the issue it duplicates.
-> - If there is a sound reason for not fixing the issue (difficulty, ABI, open
->   research questions, etc.), add the `wontfix` label and a comment explaining
->   why no changes are expected.
-> - If there is a specific and plausible reason to think that a given bug is
->   otherwise inapplicable or obsolete. One example is an open bug that doesn't
->   contain enough information to clearly understand the problem being reported
->   (e.g., not reproducible). It is fine to close such a bug, adding the
->   `worksforme` label and leaving a comment to encourage the reporter to
->   reopen the bug with more information if it's still reproducible for them.
-
-(maintenance-of-metadata)=
+- If the issue has been resolved by a particular commit, close the issue with
+  a brief comment mentioning which commit(s) fixed it. If you are authoring
+  the fix yourself, your git commit message may include the phrase
+  `Fixes #<issue number>` on a line by itself. GitHub recognizes such commit
+  messages and will automatically close the specified issue with a reference
+  to your commit.
+
+- If the reported behavior is not a bug, it is appropriate to close the issue
+  with a comment explaining why you believe it is not a bug, and adding the
+  `invalid` tag.
+
+- If the bug duplicates another issue, close it as a duplicate by adding the
+  `duplicate` label with a comment pointing to the issue it duplicates.
+
+- If there is a sound reason for not fixing the issue (difficulty, ABI, open
+  research questions, etc.), add the `wontfix` label and a comment explaining
+  why no changes are expected.
+
+- If there is a specific and plausible reason to think that a given bug is
+  otherwise inapplicable or obsolete. One example is an open bug that doesn't
+  contain enough information to clearly understand the problem being reported
+  (e.g., not reproducible). It is fine to close such a bug, adding the
+  `worksforme` label and leaving a comment to encourage the reporter to
+  reopen the bug with more information if it's still reproducible for them.
+
+(Maintenance of metadata)=
 
 ## Maintenance of metadata
 
@@ -128,4 +133,3 @@ open an issue asking to create an issue label and add the `infrastructure`
 label to the issue. The request should include a description of what the label
 is for. Alternatively, you can ask for the label to be created on the
 `#infrastructure` channel on the LLVM Discord.
-
diff --git a/llvm/docs/CIBestPractices.md b/llvm/docs/CIBestPractices.md
index 47b457186be22..84f88b88b7791 100644
--- a/llvm/docs/CIBestPractices.md
+++ b/llvm/docs/CIBestPractices.md
@@ -191,4 +191,3 @@ This prevents images from getting updated when the tag changes, which can
 introduce security issues and unwanted behavior differences. This incurs
 some additional burden for updates, but most of these are automatically handled
 by Renovate.
-
diff --git a/llvm/docs/CodeReview.md b/llvm/docs/CodeReview.md
index b18b4e29c87c9..df7a2b3aee8b6 100644
--- a/llvm/docs/CodeReview.md
+++ b/llvm/docs/CodeReview.md
@@ -1,4 +1,4 @@
-(code-review-policy)=
+(code_review_policy)=
 
 # LLVM Code-Review Policy and Practices
 
@@ -33,7 +33,7 @@ Please note that the developer responsible for a patch is also
 responsible for making all necessary review-related changes, including
 those requested during any post-commit review.
 
-(post-commit-review)=
+(post_commit_review)=
 
 ### Can Code Be Reviewed After It Is Committed?
 
@@ -134,7 +134,7 @@ from specific performance tests), please explain as many of these up front as
 possible. This allows the patch author and reviewers to make the most efficient
 use of their time.
 
-(lgtm-how-a-patch-is-accepted)=
+(lgtm_how_a_patch_is_accepted)=
 
 ### LGTM - How a Patch Is Accepted
 
@@ -275,4 +275,3 @@ When upstreaming changes originally written by someone else:
 - Invite the original author(s) to review the changes, and also include
   additional reviewers. Specifically, an LGTM from a (co-)author should not be
   taken as approval to land a change.
-
diff --git a/llvm/docs/ConvergenceAndUniformity.md b/llvm/docs/ConvergenceAndUniformity.md
index 91d2ab8ac3fe5..7c8bd86c61410 100644
--- a/llvm/docs/ConvergenceAndUniformity.md
+++ b/llvm/docs/ConvergenceAndUniformity.md
@@ -36,12 +36,12 @@ parallel environment. To eliminate this assumption:
 
 This document describes a static analysis for determining convergence at each
 instruction in a function. The analysis extends previous work on divergence
-analysis [^cite_divergencespmd] to cover irreducible control-flow. The described
+analysis [^DivergenceSPMD] to cover irreducible control-flow. The described
 analysis is used in LLVM to implement a UniformityAnalysis that determines the
 uniformity of value(s) computed at each instruction in an LLVM IR or MIR
 function.
 
-[^cite_divergencespmd]: Julian Rosemann, Simon Moll, and Sebastian
+[^DivergenceSPMD]: Julian Rosemann, Simon Moll, and Sebastian
     Hack. 2021. An Abstract Interpretation for SPMD Divergence on
     Reducible Control Flow Graphs. Proc. ACM Program. Lang. 5, POPL,
     Article 31 (January 2021), 35 pages.
@@ -224,21 +224,22 @@ a cycle if they both previously executed the cycle header the same number of
 times after they entered that cycle. In general, this needs to account for the
 iterations of parent cycles as well.
 
-> **Maximal converged-with:**
->
-> Dynamic instances `X1` and `X2` produced by different threads
-> for the same static instance `X` are converged in the maximal
-> converged-with relation if and only if:
->
-> - `X` is not contained in any cycle, or,
->
-> - For every cycle `C` with header `H` that contains `X`:
->
->   - every dynamic instance `H1` of `H` that precedes `X1` in
->     the respective thread is convergence-before `X2`, and,
->   - every dynamic instance `H2` of `H` that precedes `X2` in
->     the respective thread is convergence-before `X1`,
->   - without assuming that `X1` is converged with `X2`.
+```{eval-rst}
+   **Maximal converged-with:**
+
+   Dynamic instances ``X1`` and ``X2`` produced by different threads
+   for the same static instance ``X`` are converged in the maximal
+   converged-with relation if and only if:
+
+   - ``X`` is not contained in any cycle, or,
+   - For every cycle ``C`` with header ``H`` that contains ``X``:
+
+     - every dynamic instance ``H1`` of ``H`` that precedes ``X1`` in
+       the respective thread is convergence-before ``X2``, and,
+     - every dynamic instance ``H2`` of ``H`` that precedes ``X2`` in
+       the respective thread is convergence-before ``X1``,
+     - without assuming that ``X1`` is converged with ``X2``.
+```
 
 :::{note}
 Cycle headers may not be unique to a given CFG if it is irreducible. Each
@@ -466,11 +467,13 @@ Each node `X` in a given CFG is reported to be m-converged if and
 only if every cycle that contains `X` satisfies the following necessary
 conditions:
 
-> 1. Every divergent branch inside the cycle satisfies the
->    {ref}`diverged entry criterion<convergence-diverged-entry>`, and,
-> 2. There are no {ref}`diverged paths reaching the
->    cycle<convergence-diverged-outside>` from a divergent branch
->    outside it.
+```{eval-rst}
+  1. Every divergent branch inside the cycle satisfies the
+     :ref:`diverged entry criterion<convergence-diverged-entry>`, and,
+  2. There are no :ref:`diverged paths reaching the
+     cycle<convergence-diverged-outside>` from a divergent branch
+     outside it.
+```
 
 :::{note}
 A reducible cycle {ref}`trivially satisfies
@@ -618,14 +621,16 @@ an outer cycle that contains `C`.
 Thus, the diverged entry criterion can be conservatively simplified
 as follows:
 
-> For a divergent branch `B` and its join node `J`, the nodes in a
-> cycle `C` that contains both `B` and `J` are m-converged only
-> if:
->
-> - `B` strictly dominates `J`, or,
-> - The header `H` of `C` strictly dominates `J`, or,
-> - Recursively, there is cycle `C'` inside `C` that satisfies the
->   same condition.
+```{eval-rst}
+  For a divergent branch ``B`` and its join node ``J``, the nodes in a
+  cycle ``C`` that contains both ``B`` and ``J`` are m-converged only
+  if:
+
+  - ``B`` strictly dominates ``J``, or,
+  - The header ``H`` of ``C`` strictly dominates ``J``, or,
+  - Recursively, there is cycle ``C'`` inside ``C`` that satisfies the
+    same condition.
+```
 
 When `J` is the same as `H` or `B`, the trivial dominance is
 insufficient to make any statement about entries to diverged paths.
@@ -709,4 +714,3 @@ relation over dynamic instances and a {ref}`controlled m-converged
 <controlled_m_converged>` property of static instances. The {ref}`uniformity
 analysis <uniformity-analysis>` implemented in LLVM includes this for targets
 that support convergence control tokens.
-
diff --git a/llvm/docs/ConvergentOperations.md b/llvm/docs/ConvergentOperations.md
index 1605ab82da152..58f1b891083b3 100644
--- a/llvm/docs/ConvergentOperations.md
+++ b/llvm/docs/ConvergentOperations.md
@@ -43,7 +43,7 @@ relevant for deciding the correctness of generic program transforms and
 convergence-related analyses such as {ref}`uniformity analysis
 <convergence-and-uniformity>`.
 
-(convergent-operations)=
+(convergent_operations)=
 
 ## Convergent Operations
 
@@ -108,9 +108,11 @@ an undefined value.
 That is, the `textureSample` operation fits our definition of a convergent
 operation:
 
-> 1. It communicates with a set of threads that implicitly depends on control
->    flow.
-> 2. Correctness depends on this set of threads.
+```{eval-rst}
+ 1. It communicates with a set of threads that implicitly depends on control
+    flow.
+ 2. Correctness depends on this set of threads.
+```
 
 The compiler frontend can emit IR that expresses the convergence constraints as
 follows:
@@ -148,7 +150,7 @@ e.g. by leaning on target-specific callbacks that can analyze the program with
 additional knowledge, that `%condition` is always uniform across the threads
 referenced by the *convergence token* `%entry`.)
 
-(convergence-example-reductions)=
+(convergence_example_reductions)=
 
 ### Reductions inside divergent control flow
 
@@ -319,7 +321,7 @@ the `@subgroupControlBarrier` call communicates with the subset of S that
 actually reaches the call site. This set of threads doesn't change after
 jump-threading, so the answer to the question posed above remains the same.
 
-(opportunistic-convergence)=
+(opportunistic_convergence)=
 
 ### Opportunistic convergent operations
 
@@ -405,7 +407,7 @@ that certain transforms which are usually forbidden by the presence of
 convergent operations are in fact allowed, as long as they don't break up the
 region of code that is controlled by the anchor.
 
-(convergence-high-level-break)=
+(convergence_high-level_break)=
 
 ### Extended Cycles: Divergent Exit from a Loop
 
@@ -491,7 +493,7 @@ call to the {ref}`llvm.experimental.convergence.loop
 effectively extended to include all uses of this token that lie outside the
 cycle.
 
-(dynamic-instances-and-convergence-tokens)=
+(dynamic_instances_and_convergence_tokens)=
 
 ## Dynamic Instances and Convergence Tokens
 
@@ -551,7 +553,7 @@ that case is the purpose of {ref}`llvm.experimental.convergence.loop
 <llvm.experimental.convergence.loop>`.
 :::
 
-(convergence-control-intrinsics)=
+(convergence_control_intrinsics)=
 
 ## Convergence Control Intrinsics
 
@@ -561,7 +563,7 @@ produce convergence tokens.
 Behaviour is undefined if a convergence control intrinsic is called
 indirectly.
 
-(llvm-experimental-convergence-entry)=
+(llvm.experimental.convergence.entry)=
 
 ### `llvm.experimental.convergence.entry`
 
@@ -572,22 +574,23 @@ token @llvm.experimental.convergence.entry() convergent readnone
 This intrinsic is used to tie the dynamic instances inside a function to
 those in the caller.
 
+```{eval-rst}
 1. If the function is called from outside the scope of LLVM, the convergence of
    dynamic instances of this intrinsic is environment-defined. For example:
 
-   1. In an OpenCL *kernel launch*, the maximal set of threads that
+   a. In an OpenCL *kernel launch*, the maximal set of threads that
       can communicate outside the memory model is a *workgroup*.
       Hence, a suitable choice is to specify that all the threads from
       a single workgroup in OpenCL execute converged dynamic instances
       of this intrinsic.
-   2. In a C/C++ program, threads are launched independently and can
+   b. In a C/C++ program, threads are launched independently and can
       communicate only through the memory model. Hence the dynamic instances of
       this intrinsic in a C/C++ program are never converged.
-
 2. If the function is called from a call-site in LLVM IR, then two
    threads execute converged dynamic instances of this intrinsic if and
    only if both threads entered the function by executing converged
    dynamic instances of the call-site.
+```
 
 This intrinsic can occur at most once in a function, and only in the entry
 block of the function. If this intrinsic occurs in a basic block, then it must
@@ -628,7 +631,7 @@ void main() {
 }
 ```
 
-(llvm-experimental-convergence-loop)=
+(llvm.experimental.convergence.loop)=
 
 ### `llvm.experimental.convergence.loop`
 
@@ -654,7 +657,7 @@ call to this intrinsic.
 If this intrinsic occurs in a basic block, then it must precede any other
 convergent operation in the same basic block.
 
-(convergence-cycle-heart)=
+(convergence_cycle_heart)=
 
 **Heart of a Cycle:**
 
@@ -673,7 +676,7 @@ convergent operation in the same basic block.
 > this situation since its practical application is very rare.
 > :::
 
-(llvm-experimental-convergence-anchor)=
+(llvm.experimental.convergence.anchor)=
 
 ### `llvm.experimental.convergence.anchor`
 
@@ -695,7 +698,7 @@ can detect the maximal set of threads that can communicate efficiently within
 some local region of the program.
 :::
 
-(convergence-uncontrolled)=
+(convergence_uncontrolled)=
 
 ## Uncontrolled Convergent Operations
 
@@ -766,7 +769,7 @@ mentioned property:
    <llvm.experimental.convergence.entry>`; otherwise `D` is the heart of the
    parent cycle of `X`.
 
-(convergence-static-rules)=
+(convergence_static_rules)=
 
 ## Static Rules
 
@@ -803,7 +806,7 @@ for cycles instead of closed paths. Briefly, any closed path that violates
 one or more of the above static rules is contained in a cycle that also
 violates the same rule(s).
 
-(convergence-region)=
+(convergence_region)=
 
 ### Convergence Regions
 
@@ -824,7 +827,7 @@ definition `D`" to actually refer to the convergence region of the token
 `T` defined by `D`.
 :::
 
-(inferring-noconvergent)=
+(inferring_noconvergent)=
 
 ## Inferring non-convergence
 
@@ -890,47 +893,48 @@ All this affects the {ref}`maximal converged-with relation
 property <uniformity-analysis>` of static instances in the convergence region of
 `D`.
 
-(controlled-maximal-converged-with)=
+(controlled_maximal_converged_with)=
+
+```{eval-rst}
+  **Controlled Maximal converged-with Relation**
+
+  1. Dynamic instances of a *convergent operation* are related in the controlled
+     maximal converged-with relation according to the semantics of the convergence
+     control tokens.
+  2. Dynamic instances ``X1`` and ``X2`` produced by different threads for the
+     same *non-convergent operation* ``X`` are related in the controlled maximal
+     converged-with relation if and only if:
+
+     1. Both threads executed converged dynamic instances of every token
+        definition ``D`` such that ``X`` is in the convergence region of ``D``,
+        and,
+     2. Either ``X`` is not contained in any cycle, or, for every cycle ``C``
+        with header ``H`` that contains ``X``:
+
+        - every dynamic instance ``H1`` of ``H`` that precedes ``X1`` in the
+          respective thread is convergence-before ``X2``, and,
+        - every dynamic instance ``H2`` of ``H`` that precedes ``X2`` in the
+          respective thread is convergence-before ``X1``,
+        - without assuming that ``X1`` is converged with ``X2``.
+```
 
-> **Controlled Maximal converged-with Relation**
->
-> 1. Dynamic instances of a *convergent operation* are related in the controlled
->    maximal converged-with relation according to the semantics of the convergence
->    control tokens.
->
-> 2. Dynamic instances `X1` and `X2` produced by different threads for the
->    same *non-convergent operation* `X` are related in the controlled maximal
->    converged-with relation if and only if:
->
->    1. Both threads executed converged dynamic instances of every token
->       definition `D` such that `X` is in the convergence region of `D`,
->       and,
->
->    2. Either `X` is not contained in any cycle, or, for every cycle `C`
->       with header `H` that contains `X`:
->
->       - every dynamic instance `H1` of `H` that precedes `X1` in the
->         respective thread is convergence-before `X2`, and,
->       - every dynamic instance `H2` of `H` that precedes `X2` in the
->         respective thread is convergence-before `X1`,
->       - without assuming that `X1` is converged with `X2`.
+(controlled_m_converged)=
 
-(controlled-m-converged)=
+```{eval-rst}
+  **Controlled m-converged Static Instances**
 
-> **Controlled m-converged Static Instances**
->
-> A node `X` in a given CFG is reported to be m-converged if and only if:
->
-> 1. For any token definition `D` such that `X` is inside the convergence region
->    of `D`, `D` itself is m-converged, and,
->
-> 2. Every cycle that contains `X` satisfies the following necessary
->    conditions:
->
->    1. Every divergent branch inside the cycle satisfies the {ref}`diverged
->       entry criterion<convergence-diverged-entry>`, and,
->    2. There are no {ref}`diverged paths reaching the
->       cycle<convergence-diverged-outside>` from a divergent branch outside it.
+  A node ``X`` in a given CFG is reported to be m-converged if and only if:
+
+  1. For any token definition ``D`` such that ``X`` is inside the convergence region
+     of ``D``, ``D`` itself is m-converged, and,
+  2. Every cycle that contains ``X`` satisfies the following necessary
+     conditions:
+
+     a. Every divergent branch inside the cycle satisfies the :ref:`diverged
+        entry criterion<convergence-diverged-entry>`, and,
+     b. There are no :ref:`diverged paths reaching the
+        cycle<convergence-diverged-outside>` from a divergent branch outside it.
+```
 
 ### Temporal Divergence at Cycle Exit
 
@@ -1567,4 +1571,3 @@ if (condition) {
   use(%a, %b)
 }
 ```
-
diff --git a/llvm/docs/DependenceGraphs/index.md b/llvm/docs/DependenceGraphs/index.md
index b47e574969523..aa2d6f032f6b9 100644
--- a/llvm/docs/DependenceGraphs/index.md
+++ b/llvm/docs/DependenceGraphs/index.md
@@ -1,16 +1,21 @@
+---
+myst:
+  footnote_transition: false
+---
+
 # Dependence Graphs in LLVM
 
 ## Introduction
 
 Dependence graphs are useful tools in compilers for analyzing relationships
 between various program elements to help guide optimizations. The ideas
-behind these graphs are described in papers [^footnote-1] and [^footnote-2].
+behind these graphs are described in papers [^id6] and [^id7].
 
 The implementation of these ideas in LLVM may be slightly different than
 what is mentioned in the papers. These differences are documented in
-the [implementation details][implementation-details].
+the [implementation details](#implementation-details).
 
-(datadependencegraph)=
+(DataDependenceGraph)=
 
 ## Data Dependence Graph
 
@@ -21,7 +26,7 @@ It is also possible to combine some atomic nodes that have a simple
 def-use dependency between them into larger nodes that contain multiple-
 instructions.
 
-As described in [^footnote-1] the DDG uses graph abstraction to group nodes
+As described in [^id6] the DDG uses graph abstraction to group nodes
 that are part of a strongly connected component of the graph
 into special nodes called pi-blocks. pi-blocks represent cycles of data
 dependency that prevent reordering transformations. Since any strongly
@@ -43,14 +48,12 @@ itself creating a cycle in the DDG. The figure below illustrates
 how the cycle of dependency is carried through multiple def-use relations
 and a memory access dependency.
 
-```{image} cycle.png
-```
+![](cycle.png)
 
 The DDG corresponding to this example would have a pi-block that contains
 all the nodes participating in the cycle, as shown below:
 
-```{image} cycle_pi.png
-```
+![](cycle_pi.png)
 
 ## Program Dependence Graph
 
@@ -67,8 +70,7 @@ The DDG and the PDG are both directed graphs and they extend the
 node and edge types resulting in the inheritance relationship depicted
 in the UML diagram below:
 
-```{image} uml_nodes_and_edges.png
-```
+![](uml_nodes_and_edges.png)
 
 ### Graph Construction
 
@@ -87,8 +89,7 @@ from its concrete representation.
 The following UML diagram depicts the overall structure of the design
 pattern as it applies to the dependence graph implementation.
 
-```{image} uml_builder_pattern.png
-```
+![](uml_builder_pattern.png)
 
 Notice that the common code for building the two types of graphs are
 provided in the `DependenceGraphBuilder` class, while the `DDGBuilder`
@@ -103,32 +104,30 @@ implementation.
 
 #### Advantages:
 
-> - Builder allows graph construction code to be reused for DDG and PDG.
-> - Builder allows us to create DDG and PDG as separate graphs.
-> - DDG nodes and edges are completely disjoint from PDG nodes and edges allowing them to change easily and independently.
+- Builder allows graph construction code to be reused for DDG and PDG.
+- Builder allows us to create DDG and PDG as separate graphs.
+- DDG nodes and edges are completely disjoint from PDG nodes and edges allowing them to change easily and independently.
 
 #### Disadvantages:
 
-> - Builder may be perceived as over-engineering at first.
->
-> - There are some similarities between DDG nodes and edges compared to PDG nodes and edges, but there is little reuse of the class definitions.
->
->   - This is tolerable given that the node and edge types are fairly simple and there is little code reuse opportunity anyway.
+- Builder may be perceived as over-engineering at first.
+- There are some similarities between DDG nodes and edges compared to PDG nodes and edges, but there is little reuse of the class definitions.
+
+  - This is tolerable given that the node and edge types are fairly simple and there is little code reuse opportunity anyway.
 
 (implementation-details)=
 
 ## Implementation Details
 
 The current implementation of DDG differs slightly from the dependence
-graph described in [^footnote-1] in the following ways:
+graph described in [^id6] in the following ways:
 
-> 1. The graph nodes in the paper represent three main program components, namely *assignment statements*, *for loop headers* and *while loop headers*. In this implementation, DDG nodes naturally represent LLVM IR instructions. An assignment statement in this implementation typically involves a node representing the `store` instruction along with a number of individual nodes computing the right-hand-side of the assignment that connect to the `store` node via a def-use edge. The loop header instructions are not represented as special nodes in this implementation because they have limited uses and can be easily identified, for example, through `LoopAnalysis`.
-> 2. The paper describes five types of dependency edges between nodes namely *loop dependency*, *flow-*, *anti-*, *output-*, and *input-* dependencies. In this implementation *memory* edges represent the *flow-*, *anti-*, *output-*, and *input-* dependencies. However, *loop dependencies* are not made explicit, because they mainly represent association between a loop structure and the program elements inside the loop and this association is fairly obvious in LLVM IR itself.
-> 3. The paper describes two types of pi-blocks; *recurrences* whose bodies are SCCs and *IN* nodes whose bodies are not part of any SCC. In this implementation, pi-blocks are only created for *recurrences*. *IN* nodes remain as simple DDG nodes in the graph.
+1. The graph nodes in the paper represent three main program components, namely *assignment statements*, *for loop headers* and *while loop headers*. In this implementation, DDG nodes naturally represent LLVM IR instructions. An assignment statement in this implementation typically involves a node representing the `store` instruction along with a number of individual nodes computing the right-hand-side of the assignment that connect to the `store` node via a def-use edge. The loop header instructions are not represented as special nodes in this implementation because they have limited uses and can be easily identified, for example, through `LoopAnalysis`.
+2. The paper describes five types of dependency edges between nodes namely *loop dependency*, *flow-*, *anti-*, *output-*, and *input-* dependencies. In this implementation *memory* edges represent the *flow-*, *anti-*, *output-*, and *input-* dependencies. However, *loop dependencies* are not made explicit, because they mainly represent association between a loop structure and the program elements inside the loop and this association is fairly obvious in LLVM IR itself.
+3. The paper describes two types of pi-blocks; *recurrences* whose bodies are SCCs and *IN* nodes whose bodies are not part of any SCC. In this implementation, pi-blocks are only created for *recurrences*. *IN* nodes remain as simple DDG nodes in the graph.
 
 ### References
 
-[^footnote-1]: "D. J. Kuck, R. H. Kuhn, D. A. Padua, B. Leasure, and M. Wolfe (1981). DEPENDENCE GRAPHS AND COMPILER OPTIMIZATIONS."
-
-[^footnote-2]: "J. FERRANTE (IBM), K. J. OTTENSTEIN (Michigan Technological University) and JOE D. WARREN (Rice University), 1987. The Program Dependence Graph and Its Use in Optimization."
+[^id6]: "D. J. Kuck, R. H. Kuhn, D. A. Padua, B. Leasure, and M. Wolfe (1981). DEPENDENCE GRAPHS AND COMPILER OPTIMIZATIONS."
 
+[^id7]: "J. FERRANTE (IBM), K. J. OTTENSTEIN (Michigan Technological University) and JOE D. WARREN (Rice University), 1987. The Program Dependence Graph and Its Use in Optimization."
diff --git a/llvm/docs/FaultMaps.md b/llvm/docs/FaultMaps.md
index ba712013e5bdc..fe4c12a5c38d3 100644
--- a/llvm/docs/FaultMaps.md
+++ b/llvm/docs/FaultMaps.md
@@ -48,9 +48,9 @@ FunctionInfo[NumFunctions] {
 FailtKind describes the reason of expected fault. Currently three kind
 of faults are supported:
 
-> 1. `FaultMaps::FaultingLoad` - fault due to load from memory.
-> 2. `FaultMaps::FaultingLoadStore` - fault due to instruction load and store.
-> 3. `FaultMaps::FaultingStore` - fault due to store to memory.
+1. `FaultMaps::FaultingLoad` - fault due to load from memory.
+2. `FaultMaps::FaultingLoadStore` - fault due to instruction load and store.
+3. `FaultMaps::FaultingStore` - fault due to store to memory.
 
 ## The `ImplicitNullChecks` pass
 
@@ -106,11 +106,12 @@ null checks via code patching or recompilation. It follows that there
 are two requirements an explicit null check needs to satisfy for it to
 be profitable to convert it to an implicit null check:
 
-> 1. The case where the pointer is actually null (i.e. the "failing"
->    case) is extremely rare.
-> 2. The failing path heals the implicit null check into an explicit
->    null check so that the application does not repeatedly page
->    fault.
+1. The case where the pointer is actually null (i.e. the "failing"
+   case) is extremely rare.
+
+2. The failing path heals the implicit null check into an explicit
+   null check so that the application does not repeatedly page
+   fault.
 
 The frontend is expected to mark branches that satisfy (1) and (2)
 using a `!make.implicit` metadata node (the actual content of the
@@ -120,4 +121,3 @@ conversion into implicit null checks.
 
 (Note that while we could deal with (1) using profiling data, dealing
 with (2) requires some information not present in branch profiles.)
-
diff --git a/llvm/docs/FuzzingLLVM.md b/llvm/docs/FuzzingLLVM.md
index 9cbf863b9ad10..c03117d5be63d 100644
--- a/llvm/docs/FuzzingLLVM.md
+++ b/llvm/docs/FuzzingLLVM.md
@@ -1,8 +1,9 @@
 ---
-substitutions:
-  LLVM IR fuzzer: '{ref}`structured LLVM IR fuzzer <fuzzing-llvm-ir>`'
-  generic fuzzer: '{ref}`generic fuzzer <fuzzing-llvm-generic>`'
-  protobuf fuzzer: '{ref}`libprotobuf-mutator based fuzzer <fuzzing-llvm-protobuf>`'
+myst:
+  substitutions:
+    llvm_ir_fuzzer: '{ref}`structured LLVM IR fuzzer <fuzzing-llvm-ir>`'
+    generic_fuzzer: '{ref}`generic fuzzer <fuzzing-llvm-generic>`'
+    protobuf_fuzzer: '{ref}`libprotobuf-mutator based fuzzer <fuzzing-llvm-protobuf>`'
 ---
 
 # Fuzzing LLVM libraries and tools
@@ -17,13 +18,13 @@ fuzzers, see {ref}`building-fuzzers`.
 
 ### clang-fuzzer
 
-A {{ generic fuzzer }} that tries to compile textual input as C++ code. Some of the
+A {{ generic_fuzzer }} that tries to compile textual input as C++ code. Some of the
 bugs this fuzzer has reported are [on bugzilla](https://llvm.org/pr23057) and [on OSS Fuzz's
 tracker](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+clang-fuzzer).
 
 ### clang-proto-fuzzer
 
-A {{ protobuf fuzzer }} that compiles valid C++ programs generated from a protobuf
+A {{ protobuf_fuzzer }} that compiles valid C++ programs generated from a protobuf
 class that describes a subset of the C++ language.
 
 This fuzzer accepts clang command-line options after `ignore_remaining_args=1`.
@@ -36,30 +37,30 @@ level:
 
 ### clang-format-fuzzer
 
-A {{ generic fuzzer }} that runs [clang-format][clang-format] on C++ text fragments. Some of the
+A {{ generic_fuzzer }} that runs [clang-format][clang-format] on C++ text fragments. Some of the
 bugs this fuzzer has reported are [on bugzilla](https://llvm.org/pr23052)
 and [on OSS Fuzz's tracker](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+clang-format-fuzzer).
 
 ### llvm-as-fuzzer
 
-A {{ generic fuzzer }} that tries to parse text as {doc}`LLVM assembly <LangRef>`.
+A {{ generic_fuzzer }} that tries to parse text as {doc}`LLVM assembly <LangRef>`.
 Some of the bugs this fuzzer has reported are [on bugzilla](https://llvm.org/pr24639).
 
 ### llvm-dwarfdump-fuzzer
 
-A {{ generic fuzzer }} that interprets inputs as object files and runs
+A {{ generic_fuzzer }} that interprets inputs as object files and runs
 {doc}`llvm-dwarfdump <CommandGuide/llvm-dwarfdump>` on them. Some of the bugs
 this fuzzer has reported are [on OSS Fuzz's tracker](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj-llvm+llvm-dwarfdump-fuzzer)
 
 ### llvm-demangle-fuzzer
 
-A {{ generic fuzzer }} for the Itanium demangler used in various LLVM tools. We've
+A {{ generic_fuzzer }} for the Itanium demangler used in various LLVM tools. We've
 fuzzed \_\_cxa_demangle to death, why not fuzz LLVM's implementation of the same
 function!
 
 ### llvm-isel-fuzzer
 
-A {{ LLVM IR fuzzer }} aimed at finding bugs in instruction selection.
+A {{ llvm_ir_fuzzer }} aimed at finding bugs in instruction selection.
 
 This fuzzer accepts flags after `ignore_remaining_args=1`. The flags match
 those of {doc}`llc <CommandGuide/llc>` and the triple is required. For example,
@@ -83,7 +84,7 @@ mode, the same example could be run like so:
 
 ### llvm-opt-fuzzer
 
-A {{ LLVM IR fuzzer }} aimed at finding bugs in optimization passes.
+A {{ llvm_ir_fuzzer }} aimed at finding bugs in optimization passes.
 
 It receives an optimization pipeline and runs it for each fuzzer input.
 
@@ -105,7 +106,7 @@ might be embedded directly into the binary file name:
 
 ### llvm-mc-assemble-fuzzer
 
-A {{ generic fuzzer }} that fuzzes the MC layer's assemblers by treating inputs as
+A {{ generic_fuzzer }} that fuzzes the MC layer's assemblers by treating inputs as
 target-specific assembly.
 
 Note that this fuzzer has an unusual command line interface which is not fully
@@ -121,7 +122,7 @@ This scheme will likely change in the future.
 
 ### llvm-mc-disassemble-fuzzer
 
-A {{ generic fuzzer }} that fuzzes the MC layer's disassemblers by treating inputs
+A {{ generic_fuzzer }} that fuzzes the MC layer's disassemblers by treating inputs
 as assembled binary data.
 
 Note that this fuzzer has an unusual command line interface which is not fully
@@ -130,7 +131,7 @@ compatible with all of libFuzzer's features. See the notes above about
 
 ### lldb-target-fuzzer
 
-A {{ generic fuzzer }} that interprets inputs as object files and uses them to
+A {{ generic_fuzzer }} that interprets inputs as object files and uses them to
 create a target in lldb.
 
 ## Mutators and Input Generators
@@ -149,9 +150,9 @@ mutations. This type of fuzzer is good for stressing the surface layers of a
 program, and is good at testing things like lexers, parsers, or binary
 protocols.
 
-Some of the in-tree fuzzers that use this type of mutator are [clang-fuzzer],
-[clang-format-fuzzer], [llvm-as-fuzzer], [llvm-dwarfdump-fuzzer],
-[llvm-mc-assemble-fuzzer], and [llvm-mc-disassemble-fuzzer].
+Some of the in-tree fuzzers that use this type of mutator are [clang-fuzzer](#clang-fuzzer),
+[clang-format-fuzzer](#clang-format-fuzzer), [llvm-as-fuzzer](#llvm-as-fuzzer), [llvm-dwarfdump-fuzzer](#llvm-dwarfdump-fuzzer),
+[llvm-mc-assemble-fuzzer](#llvm-mc-assemble-fuzzer), and [llvm-mc-disassemble-fuzzer](#llvm-mc-disassemble-fuzzer).
 
 (fuzzing-llvm-protobuf)=
 
@@ -166,12 +167,12 @@ interesting than parser error handling.
 
 To build this kind of fuzzer you need [protobuf][protobuf] and its dependencies
 installed, and you need to specify some extra flags when configuring the build
-with {doc}`CMake <CMake>`. For example, [clang-proto-fuzzer] can be enabled by
+with {doc}`CMake <CMake>`. For example, [clang-proto-fuzzer](#clang-proto-fuzzer) can be enabled by
 adding `-DCLANG_ENABLE_PROTO_FUZZER=ON` to the flags described in
 {ref}`building-fuzzers`.
 
 The only in-tree fuzzer that uses `libprotobuf-mutator` today is
-[clang-proto-fuzzer].
+[clang-proto-fuzzer](#clang-proto-fuzzer).
 
 (fuzzing-llvm-ir)=
 
@@ -182,7 +183,7 @@ We also use a more direct form of structured fuzzing for fuzzers that take
 library, which was [discussed at EuroLLVM 2017][discussed at eurollvm 2017].
 
 The `FuzzMutate` library is used to structurally fuzz backends in
-[llvm-isel-fuzzer].
+[llvm-isel-fuzzer](#llvm-isel-fuzzer).
 
 ## Building and Running
 
@@ -242,4 +243,3 @@ enable standalone testing.
 [llvm-bugs mailing list]: http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs
 [oss fuzz]: https://github.com/google/oss-fuzz
 [protobuf]: https://github.com/google/protobuf
-
diff --git a/llvm/docs/GetElementPtr.md b/llvm/docs/GetElementPtr.md
index 9571bd27daa70..44735302c64fc 100644
--- a/llvm/docs/GetElementPtr.md
+++ b/llvm/docs/GetElementPtr.md
@@ -3,7 +3,7 @@
 ## Introduction
 
 This document seeks to dispel the mystery and confusion surrounding LLVM's
-[GetElementPtr](LangRef.html#getelementptr-instruction) (GEP) instruction.
+[GetElementPtr](LangRef.md#getelementptr-instruction) (GEP) instruction.
 Questions about the wily GEP instruction are probably the most frequent
 questions once a developer gets down to coding with LLVM. Here we lay
 out the sources of confusion and show that the GEP instruction is really quite
@@ -45,7 +45,7 @@ X = &Foo[0].F;
 
 Sometimes this question gets rephrased as:
 
-(gep-index-through-first-pointer)=
+(GEP index through first pointer)=
 
 > *Why is it okay to index through the first pointer, but subsequent pointers
 > won't be dereferenced?*
@@ -255,7 +255,7 @@ Also, GEP carries additional pointer aliasing rules. It's invalid to take a GEP
 from one object, address into a different separately allocated object, and
 dereference it. IR producers (front-ends) must follow this rule, and consumers
 (optimizers, specifically alias analysis) benefit from being able to rely on
-it. See the [Rules] section for more information.
+it. See the {ref}`Rules <Rules>` section for more information.
 
 And, GEP is more concise in common cases.
 
@@ -291,7 +291,7 @@ you want to support VLAs, your code will have to be prepared to reverse-engineer
 the linearization. One way to solve this problem is to use the ScalarEvolution
 library, which always presents VLA and non-VLA indexing in the same manner.
 
-(rules)=
+(Rules)=
 
 ## Rules
 
@@ -389,7 +389,7 @@ because LLVM has no restrictions on mixing types in addressing, loads or stores.
 LLVM's type-based alias analysis pass uses metadata to describe a different type
 system (such as the C type system), and performs type-based aliasing on top of
 that. Further details are in the
-[language reference](LangRef.html#tbaa-metadata).
+[language reference](LangRef.md#tbaa-metadata).
 
 ### What happens if a GEP computation overflows?
 
@@ -433,8 +433,8 @@ priority:
 - Support C, C-like languages, and languages which can be conceptually lowered
   into C (this covers a lot).
 - Support optimizations such as those that are common in C compilers. In
-  particular, GEP is a cornerstone of LLVM's [pointer aliasing
-  model](LangRef.html#pointeraliasing).
+  particular, GEP is a cornerstone of LLVM's {ref}`pointer aliasing
+  model <pointeraliasing>`.
 - Provide a consistent method for computing addresses so that address
   computations don't need to be a part of load and store instructions in the IR.
 - Support non-C-like languages, to the extent that it doesn't interfere with
@@ -478,4 +478,3 @@ instruction:
    types of the pointers.
 5. Leading zero indices are not superfluous for pointer aliasing nor the types
    of the pointers.
-
diff --git a/llvm/docs/GitHubActionsRunners.md b/llvm/docs/GitHubActionsRunners.md
index d5584a90aef71..c077247c8b0dd 100644
--- a/llvm/docs/GitHubActionsRunners.md
+++ b/llvm/docs/GitHubActionsRunners.md
@@ -1,9 +1,5 @@
 # LLVM GitHub Actions Runners
 
-```{contents}
-:local: true
-```
-
 ## Overview
 
 LLVM's GitHub Actions workflows run on two kinds of runners:
@@ -93,4 +89,3 @@ For example:
 
 Version-specific formulae (for example `python at 3.12`) can be used when a job
 needs a particular version of a tool.
-
diff --git a/llvm/docs/GwpAsan.md b/llvm/docs/GwpAsan.md
index 33a5dd79f0afb..87b85845dc83b 100644
--- a/llvm/docs/GwpAsan.md
+++ b/llvm/docs/GwpAsan.md
@@ -107,7 +107,7 @@ slots are randomly reused to guard future allocations.
 ## Usage
 
 GWP-ASan already ships by default in the
-[Scudo Hardened Allocator](https://llvm.org/docs/ScudoHardenedAllocator.html),
+{doc}`Scudo Hardened Allocator <ScudoHardenedAllocator>`,
 so building with `-fsanitize=scudo` is the quickest and easiest way to try out
 GWP-ASan.
 
@@ -130,8 +130,8 @@ several aspects of GWP-ASan to be configured through the following methods:
   default visibility. This will override the compile time define;
 - Depending on allocator support (Scudo has support for this mechanism): Through
   an environment variable, containing the options string to be parsed. In Scudo,
-  this is through `SCUDO_OPTIONS=GWP_ASAN_${OPTION_NAME}=${VALUE}` (e.g.
-  `SCUDO_OPTIONS=GWP_ASAN_SampleRate=100`). Options defined this way will
+  this is through {title-reference}`SCUDO_OPTIONS=GWP_ASAN_${OPTION_NAME}=${VALUE}` (e.g.
+  {title-reference}`SCUDO_OPTIONS=GWP_ASAN_SampleRate=100`). Options defined this way will
   override any definition made through `__gwp_asan_default_options`.
 
 The options string follows a syntax similar to ASan, where distinct options
@@ -266,4 +266,3 @@ $ cat my_gwp_asan_error.txt | symbolize.sh
 | *** End GWP-ASan report ***
 | Segmentation fault
 ```
-
diff --git a/llvm/docs/HowToAddABuilder.md b/llvm/docs/HowToAddABuilder.md
index b7b5483a11193..0005ea957224b 100644
--- a/llvm/docs/HowToAddABuilder.md
+++ b/llvm/docs/HowToAddABuilder.md
@@ -87,12 +87,12 @@ Here are the steps you can follow to do so:
     Creating a worker](http://docs.buildbot.net/current/tutorial/firstrun.html#creating-a-worker)
     for more details) by running the following command:
 
-    > ```bash
-    > $ buildbot-worker create-worker <buildbot-worker-root-directory> \
-    >              lab.llvm.org:9994 \
-    >              <buildbot-worker-access-name> \
-    >              <buildbot-worker-access-password>
-    > ```
+    ```bash
+    $ buildbot-worker create-worker <buildbot-worker-root-directory> \
+                 lab.llvm.org:9994 \
+                 <buildbot-worker-access-name> \
+                 <buildbot-worker-access-password>
+    ```
 
     Only once a new worker is stable, and
     approval from Galina has been received (see last step) should it
@@ -100,9 +100,9 @@ Here are the steps you can follow to do so:
 
     Now start the worker:
 
-    > ```bash
-    > $ buildbot-worker start <buildbot-worker-root-directory>
-    > ```
+    ```bash
+    $ buildbot-worker start <buildbot-worker-root-directory>
+    ```
 
     This will cause your new worker to connect to the staging buildmaster
     which is silent by default.
@@ -131,7 +131,7 @@ Here are the steps you can follow to do so:
 
 08. Send a patch which adds your build worker and your builder to
     [zorg](https://github.com/llvm/llvm-zorg). Use the typical LLVM
-    [workflow](https://llvm.org/docs/Contributing.html#how-to-submit-a-patch).
+    {ref}`workflow <submit_patch>`.
 
     - workers are added to `buildbot/osuosl/master/config/workers.py`
     - builders are added to `buildbot/osuosl/master/config/builders.py`
@@ -198,43 +198,43 @@ In order to use this "local testing" mode:
 - Create and activate a Python [venv](https://docs.python.org/3/library/venv.html) and install the necessary
   dependencies. This step can be run from any directory.
 
-  > ```bash
-  > python -m venv bbenv
-  > source bbenv/bin/activate
-  > pip install buildbot{,-console-view,-grid-view,-waterfall-view,-worker,-www}==3.11.7 urllib3
-  > ```
+  ```bash
+  python -m venv bbenv
+  source bbenv/bin/activate
+  pip install buildbot{,-console-view,-grid-view,-waterfall-view,-worker,-www}==3.11.7 urllib3
+  ```
 
 - If your system has Python 3.13 or newer you will need to additionally
   install `legacy-cgi` and make a minor patch to the installed buildbot
   package. This step does not need to be followed for earlier Python versions.
 
-  > ```bash
-  > pip install legacy-cgi
-  > sed -i \
-  >   -e 's/import pipes/import shlex/' \
-  >   -e 's/pipes\.quote/shlex.quote/' \
-  >   bbenv/lib/python3.13/site-packages/buildbot_worker/runprocess.py
-  > ```
+  ```bash
+  pip install legacy-cgi
+  sed -i \
+    -e 's/import pipes/import shlex/' \
+    -e 's/pipes\.quote/shlex.quote/' \
+    bbenv/lib/python3.13/site-packages/buildbot_worker/runprocess.py
+  ```
 
 - Initialise the necessary buildmaster files, link to the configuration in a
   local checkout out of [llvm-zorg](https://github.com/llvm/llvm-zorg), and
   ask `buildbot` to check the configuration. This step can be run from any
   directory.
 
-  > ```bash
-  > buildbot create-master llvm-testbbmaster
-  > cd llvm-testbbmaster
-  > ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/master.cfg .
-  > ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/config/ .
-  > ln -s /path/to/checkout/of/llvm-zorg/zorg/ .
-  > BUILDBOT_TEST=1 buildbot checkconfig
-  > ```
+  ```bash
+  buildbot create-master llvm-testbbmaster
+  cd llvm-testbbmaster
+  ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/master.cfg .
+  ln -s /path/to/checkout/of/llvm-zorg/buildbot/osuosl/master/config/ .
+  ln -s /path/to/checkout/of/llvm-zorg/zorg/ .
+  BUILDBOT_TEST=1 buildbot checkconfig
+  ```
 
 - Start the buildmaster.
 
-  > ```bash
-  > BUILDBOT_TEST=1 buildbot start --nodaemon .
-  > ```
+  ```bash
+  BUILDBOT_TEST=1 buildbot start --nodaemon .
+  ```
 
 - After waiting a few seconds for startup to complete, you should be able to
   open the web UI at `http://localhost:8011`. If there are any errors or
@@ -245,13 +245,13 @@ In order to use this "local testing" mode:
   name for the worker associated with the build configuration you want to test
   in `buildbot/osuosl/master/config/builders.py`.
 
-  > ```bash
-  > buildbot-worker create-worker <buildbot-worker-root-directory> \
-  >                 localhost:9990 \
-  >                 <buildbot-worker-name> \
-  >                 test
-  > buildbot-worker start --nodaemon <buildbot-worker-root-directory>
-  > ```
+  ```bash
+  buildbot-worker create-worker <buildbot-worker-root-directory> \
+                  localhost:9990 \
+                  <buildbot-worker-name> \
+                  test
+  buildbot-worker start --nodaemon <buildbot-worker-root-directory>
+  ```
 
 - Either wait until the poller sets off a build, or alternatively force a
   build to start in the web UI.
@@ -268,9 +268,9 @@ server the following command will suffice to make the web UI accessible via
 `http://localhost:8011` and make it possible for a local worker to connect
 to the remote buildmaster by connecting to `localhost:9900`:
 
-> ```bash
-> ssh -N -L 8011:localhost:8011 -L 9990:localhost:9990 username at buildmaster_server_address
-> ```
+```bash
+ssh -N -L 8011:localhost:8011 -L 9990:localhost:9990 username at buildmaster_server_address
+```
 
 Be aware that some build configurations may checkout the current upstream
 `llvm-zorg` repository in order to retrieve additional scripts used during
@@ -413,4 +413,3 @@ Some tasks don't give immediate feedback, so if nothing happens within a short
 time, try again with the browser's web console open. Sometimes you will see
 403 errors and other messages that might indicate you don't have the correct
 details set up.
-
diff --git a/llvm/docs/HowToReleaseLLVM.md b/llvm/docs/HowToReleaseLLVM.md
index 62bd66d5876da..acc07a302460a 100644
--- a/llvm/docs/HowToReleaseLLVM.md
+++ b/llvm/docs/HowToReleaseLLVM.md
@@ -211,18 +211,20 @@ release, all reported bugs will be deferred to the next stable release.
 
 The official release managers are:
 
-- Even releases: Tom Stellard (<mailto:tstellar at redhat.com>)
-- Odd releases: Tobias Hieta (<mailto:tobias at hieta.se>)
+- Even releases: Tom Stellard ([tstellar at redhat.com](mailto:tstellar at redhat.com))
+- Odd releases: Tobias Hieta ([tobias at hieta.se](mailto:tobias at hieta.se))
 
 The official release testers are volunteers from the community who have
 consistently validated and released binaries for their targets/OSs. To contact
 them, you should post on the [Discourse forums (Project
 Infrastructure - Release Testers).](https://discourse.llvm.org/c/infrastructure/release-testers/66)
 
-The official testers list is in the file `RELEASE_TESTERS.TXT`
-\<<https://github.com/llvm/llvm-project/blob/main/llvm/RELEASE_TESTERS.TXT>>\`\_, in
+The official testers list is in the file [`RELEASE_TESTERS.TXT`][release testers], in
 the LLVM repository.
 
+[release testers]: https://github.com/llvm/llvm-project/blob/main/llvm/RELEASE_TESTERS.TXT
+[release milestone query]: https://github.com/llvm/llvm-project/issues?q=is%3Aissue+milestone%3A%22LLVM+14.0.5+Release%22+no%3Aproject+
+
 ### Community Testing
 
 Once all testing is complete and appropriate bugs are filed, the release
@@ -282,7 +284,7 @@ This section describes how to triage bug reports:
 1. Search for bugs with a Release Milestone that have not been added to the
    "Release Status" github project:
 
-   <https://github.com/llvm/llvm-project/issues?q=is%3Aissue+milestone%3A%22LLVM+14.0.5+Release%22+no%3Aproject+>
+   [https://github.com/llvm/llvm-project/issues?q=is%3Aissue+milestone%3A%22LLVM+14.0.5+Release%22+no%3Aproject+][release milestone query]
 
    Replace 14.0.5 in this query with the version from the Release Milestone being
    targeted.
@@ -392,4 +394,3 @@ $ git log --format="- %aN: [%s (%h)](https://github.com/llvm/llvm-project/commit
 
 Once the release has been announced, add a link to the announcement on the llvm
 homepage (from the `llvm-www` repo) in the "Release Emails" section.
-
diff --git a/llvm/docs/HowToSetUpLLVMStyleRTTI.md b/llvm/docs/HowToSetUpLLVMStyleRTTI.md
index f56cd1a88614b..7e72f5bdf21d1 100644
--- a/llvm/docs/HowToSetUpLLVMStyleRTTI.md
+++ b/llvm/docs/HowToSetUpLLVMStyleRTTI.md
@@ -7,7 +7,7 @@ own hand-rolled form of RTTI which is much more efficient and flexible,
 although it requires a bit more work from you as a class author.
 
 A description of how to use LLVM-style RTTI from a client's perspective is
-given in the [Programmer's Manual](ProgrammersManual.html#isa). This
+given in the {ref}`Programmer's Manual <isa>`. This
 document, in contrast, discusses the steps you need to take as a class
 hierarchy author to make LLVM-style RTTI available to your clients.
 
@@ -588,4 +588,3 @@ if `someVal` was also `std::nullopt`.
 
 [curiously recurring template idiom]: https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
 [is-a]: http://en.wikipedia.org/wiki/Is-a
-
diff --git a/llvm/docs/HowToSubmitABug.md b/llvm/docs/HowToSubmitABug.md
index 4c23b9f134f80..8cfa41d1a3f5e 100644
--- a/llvm/docs/HowToSubmitABug.md
+++ b/llvm/docs/HowToSubmitABug.md
@@ -9,7 +9,8 @@ getting it fixed quickly.
 🔒 If you believe that the bug is security related, please follow {ref}`report-security-issue`. 🔒
 
 Basically, you have to do two things at a minimum. First, decide whether the
-bug [crashes the compiler] or if the compiler is [miscompiling] the program
+bug {ref}`crashes the compiler <crashes the compiler>` or if the compiler is
+{ref}`miscompiling <miscompiling>` the program
 (i.e., the compiler successfully produces an executable, but it doesn't run
 right). Based on what type of bug it is, follow the instructions in the
 linked section to narrow down the bug so that the person who fixes it will be
@@ -26,7 +27,7 @@ not sure). The bug description should contain the following information:
 
 Thanks for helping us make LLVM better!
 
-(crashes-the-compiler)=
+(crashes the compiler)=
 
 ## Crashing Bugs
 
@@ -109,7 +110,8 @@ potentially be much faster.
 
 :::{TIP}
 Reduction is fastest and most effective the simpler the
-reproduction script is. Ideally, this will be running `opt` with a
+reproduction script is. Ideally, this will be running
+{title-reference}`opt` with a
 single pass. The most effective way to extract the IR before a
 specific point is a two step process. First, run the testcase with
 the `-print-pass-numbers` flag. This will print the name of a
@@ -145,12 +147,13 @@ If none of these crash, please follow the instructions for a
 should be able to reduce this with {doc}`llvm-reduce
 <CommandGuide/llvm-reduce>`, similar to middle end bugs. In this
 case, your test script should use {doc}`llc <CommandGuide/llc>`
-instead of `opt`.
+instead of {title-reference}`opt`.
 
 Please run this, then file a bug with the instructions and reduced
-`.bc` file that `llvm-reduce` emits. If something goes wrong with
-`llvm-reduce`, please submit the `foo.bc` file and the option that
-`llc` crashes with.
+`.bc` file that {title-reference}`llvm-reduce` emits. If something goes wrong
+with {title-reference}`llvm-reduce`, please submit the `foo.bc` file and the
+option that
+{title-reference}`llc` crashes with.
 
 ### LTO bugs
 
@@ -225,4 +228,3 @@ reducing the inputs required to reproduce the miscompilation. The
 {doc}`OptBisect <OptBisect>` page shows how to find the optimization pass
 causing the miscompile. You can use {doc}`llvm-reduce <CommandGuide/llvm-reduce>`
 to minimize the bitcode necessary to reproduce the miscompilation.
-
diff --git a/llvm/docs/HowToUseInstrMappings.md b/llvm/docs/HowToUseInstrMappings.md
index 12bb9e64fdf45..dd928d51d4c71 100644
--- a/llvm/docs/HowToUseInstrMappings.md
+++ b/llvm/docs/HowToUseInstrMappings.md
@@ -126,7 +126,7 @@ def ADD_Pf : ALU32_rr<(outs IntRegs:$dst),
 ```
 
 In this step, we modify these instructions to include the information
-required by the relationship model, \<tt>getPredOpcode\</tt>, so that they can
+required by the relationship model, `getPredOpcode`, so that they can
 be related.
 
 ```text
@@ -165,4 +165,3 @@ to have the same value for all 3 instructions in order to be related. Next,
 with `KeyCol` and `ValueCols`. If an instruction sets its `PredSense`
 value to something not used in the relation model, it will not be assigned
 a column in the relation table.
-
diff --git a/llvm/docs/MLGO.md b/llvm/docs/MLGO.md
index 00bcb66a6c633..2088d56842b89 100644
--- a/llvm/docs/MLGO.md
+++ b/llvm/docs/MLGO.md
@@ -31,9 +31,8 @@ a bitcode file and command line flags file for each extracted module. The
 corpus structure is designed to contain sufficient information to fully
 compile the bitcode to bit-identical object files.
 
-```{eval-rst}
-.. program:: extract_ir.py
-```
+:::{program} extract_ir.py
+:::
 
 ### Synopsis
 
@@ -157,9 +156,8 @@ does not capture all object files in the build however, only the ones that
 are involved in the link for the binary passed to the `bazel aquery`
 invocation.
 
-```{eval-rst}
-.. program:: make_corpus.py
-```
+:::{program} make_corpus.py
+:::
 
 ### Synopsis
 
@@ -180,9 +178,8 @@ A list of space separated flags that are put into the corpus description.
 These are used by some tooling when compiling the modules within the corpus.
 :::
 
-```{eval-rst}
-.. program:: combine_training_corpus.py
-```
+:::{program} combine_training_corpus.py
+:::
 
 ### Synopsis
 
@@ -437,6 +434,7 @@ point vectors. These embeddings can be computed at multiple granularity levels
 (instruction, basic block, and function) and used for ML-guided compiler
 optimizations.
 
+(ir2vec-embeddings)=
 ### IR2Vec
 
 IR2Vec is a program embedding approach designed specifically for LLVM IR. It
@@ -577,8 +575,8 @@ The core components are:
 #### Using MIR2Vec
 
 :::{note}
-This section describes how to use MIR2Vec within LLVM passes. `llvm-ir2vec`
-tool \` {doc}`CommandGuide/llvm-ir2vec` can be used for generating MIR2Vec
+This section describes how to use MIR2Vec within LLVM passes. The
+{doc}`llvm-ir2vec tool <CommandGuide/llvm-ir2vec>` can be used for generating MIR2Vec
 embeddings from Machine IR files (.mir), which can be useful for generating
 embeddings outside of compiler passes.
 :::
@@ -667,7 +665,7 @@ pass `-DPython3_ROOT_DIR` to `cmake`).
 Once you install the pip package, find where it was installed:
 
 ```console
-TF_PIP=$(sudo -u buildbot python3 -c "import tensorflow as tf; import os; print(os.path.dirname(tf.__file__))")``
+TF_PIP=$(sudo -u buildbot python3 -c "import tensorflow as tf; import os; print(os.path.dirname(tf.__file__))")
 ```
 
 Then build LLVM:
@@ -711,4 +709,3 @@ optimizations that are currently MLGO-enabled, it may be used as follows:
 where the `name` is a path fragment. We will expect to find 2 files,
 `<name>.in` (readable, data incoming from the managing process) and
 `<name>.out` (writable, the model runner sends data to the managing process)
-

>From 4945b98b09a326ce65212ba106a6048143bd5d7e Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Fri, 11 Sep 2026 23:32:39 +0000
Subject: [PATCH 3/3] [LLVM][docs] Convert remaining batch 10 tables to MyST

---
 llvm/docs/ConvergenceAndUniformity.md | 436 ++++++++++++++++++++------
 llvm/docs/GwpAsan.md                  |  60 ++--
 2 files changed, 368 insertions(+), 128 deletions(-)

diff --git a/llvm/docs/ConvergenceAndUniformity.md b/llvm/docs/ConvergenceAndUniformity.md
index 7c8bd86c61410..417f0aac6467e 100644
--- a/llvm/docs/ConvergenceAndUniformity.md
+++ b/llvm/docs/ConvergenceAndUniformity.md
@@ -120,19 +120,47 @@ groups in order to share resources when possible.
 :name: convergence-natural-loop
 :::
 
-```{eval-rst}
-.. table::
-   :name: convergence-thread-example
-   :align: left
-
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-   |          |        | 1   | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   |      |
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-   | Thread 1 | Entry1 | H1  | B1  | L1  | H3  |     | L3  |     |     |     | Exit |
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-   | Thread 2 | Entry1 | H2  |     | L2  | H4  | B2  | L4  | H5  | B3  | L5  | Exit |
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-```
+:::{list-table}
+:name: convergence-thread-example
+:align: left
+
+* -
+  -
+  - 1
+  - 2
+  - 3
+  - 4
+  - 5
+  - 6
+  - 7
+  - 8
+  - 9
+  -
+* - Thread 1
+  - Entry1
+  - H1
+  - B1
+  - L1
+  - H3
+  -
+  - L3
+  -
+  -
+  -
+  - Exit
+* - Thread 2
+  - Entry1
+  - H2
+  -
+  - L2
+  - H4
+  - B2
+  - L4
+  - H5
+  - B3
+  - L5
+  - Exit
+:::
 
 In the above table, each row is a different thread, listing the
 dynamic instances produced by that thread from left to right. Each
@@ -158,21 +186,51 @@ that is defined as the transitive closure of:
    is executed strictly before `Q` in the same thread, then `P1`
    is *convergence-before* `Q`.
 
-```{eval-rst}
-.. table::
-   :name: convergence-order-example
-   :align: left
-
-   +----------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-   |          | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9    |
-   +----------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-   | Thread 1 | Entry | ... |     |     |     | S2  | T   | ... | Exit |
-   +----------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-   | Thread 2 | Entry | ... |     | Q2  | R   | S1  |     | ... | Exit |
-   +----------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-   | Thread 3 | Entry | ... | P   | Q1  |     |     |     | ... |      |
-   +----------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-```
+:::{list-table}
+:name: convergence-order-example
+:align: left
+
+* -
+  - 1
+  - 2
+  - 3
+  - 4
+  - 5
+  - 6
+  - 7
+  - 8
+  - 9
+* - Thread 1
+  - Entry
+  - ...
+  -
+  -
+  -
+  - S2
+  - T
+  - ...
+  - Exit
+* - Thread 2
+  - Entry
+  - ...
+  -
+  - Q2
+  - R
+  - S1
+  -
+  - ...
+  - Exit
+* - Thread 3
+  - Entry
+  - ...
+  - P
+  - Q1
+  -
+  -
+  -
+  - ...
+  -
+:::
 
 The above table shows partial sequences of dynamic instances from
 different threads. Dynamic instances in the same column are assumed
@@ -253,18 +311,46 @@ relation for the given cycle hierarchy".
 
 Maximal convergence can now be demonstrated in the earlier example as follows:
 
-```{eval-rst}
-.. table::
-   :align: left
-
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-   |          |        | 1   | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   |      |
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-   | Thread 1 | Entry1 | H1  | B1  | L1  | H3  |     | L3  |     |     |     | Exit |
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-   | Thread 2 | Entry2 | H2  |     | L2  | H4  | B2  | L4  | H5  | B3  | L5  | Exit |
-   +----------+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-```
+:::{list-table}
+:align: left
+
+* -
+  -
+  - 1
+  - 2
+  - 3
+  - 4
+  - 5
+  - 6
+  - 7
+  - 8
+  - 9
+  -
+* - Thread 1
+  - Entry1
+  - H1
+  - B1
+  - L1
+  - H3
+  -
+  - L3
+  -
+  -
+  -
+  - Exit
+* - Thread 2
+  - Entry2
+  - H2
+  -
+  - L2
+  - H4
+  - B2
+  - L4
+  - H5
+  - B3
+  - L5
+  - Exit
+:::
 
 - `Entry1` and `Entry2` are converged.
 - `H1` and `H2` are converged.
@@ -355,20 +441,50 @@ branches. The table below shows the convergence between three threads
 taking different paths through the CFG. Dynamic instances listed in
 the same column are converged.
 
-> ```{eval-rst}
-> .. table::
->    :align: left
+> :::{list-table}
+> :align: left
 >
->    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
->    |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 10   |
->    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
->    | Thread1 | Entry | P1  | Q1  | S1  | P3  | Q3  | R1  | S2  | Exit |
->    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
->    | Thread2 | Entry | P2  | Q2  |     |     |     | R2  | S3  | Exit |
->    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
->    | Thread3 | Entry |     |     |     |     |     | R3  | S4  | Exit |
->    +---------+-------+-----+-----+-----+-----+-----+-----+-----+------+
-> ```
+> * -
+>   - 1
+>   - 2
+>   - 3
+>   - 4
+>   - 5
+>   - 6
+>   - 7
+>   - 8
+>   - 10
+> * - Thread1
+>   - Entry
+>   - P1
+>   - Q1
+>   - S1
+>   - P3
+>   - Q3
+>   - R1
+>   - S2
+>   - Exit
+> * - Thread2
+>   - Entry
+>   - P2
+>   - Q2
+>   -
+>   -
+>   -
+>   - R2
+>   - S3
+>   - Exit
+> * - Thread3
+>   - Entry
+>   -
+>   -
+>   -
+>   -
+>   -
+>   - R3
+>   - S4
+>   - Exit
+> :::
 
 - `P2` and `P3` are not converged due to `S1`
 - `Q2` and `Q3` are not converged due to `S1`
@@ -563,18 +679,46 @@ S`.
   reconverge in the same iteration of the outer cycle `C`, but they
   may have executed the inner cycle `C'` differently.
 
-  ```{eval-rst}
-  .. table::
-     :align: left
-
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11   |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread1 | Entry | P1  | Q1  |     |     |     | R1  | S1  | P3  | ... | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread2 | Entry | P2  | Q2  | S2  | P4  | Q4  | R2  | S4  |     |     | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-  ```
+  :::{list-table}
+  :align: left
+
+  * -
+    - 1
+    - 2
+    - 3
+    - 4
+    - 5
+    - 6
+    - 7
+    - 8
+    - 9
+    - 10
+    - 11
+  * - Thread1
+    - Entry
+    - P1
+    - Q1
+    -
+    -
+    -
+    - R1
+    - S1
+    - P3
+    - ...
+    - Exit
+  * - Thread2
+    - Entry
+    - P2
+    - Q2
+    - S2
+    - P4
+    - Q4
+    - R2
+    - S4
+    -
+    -
+    - Exit
+  :::
 
   In the table above, `S2` is not converged with `S1` due to `R1`.
 
@@ -585,18 +729,43 @@ S`.
   `Q` to `S`. Informally, threads that diverge at `Q`
   reconverge at `S` in the same iteration of `C`.
 
-  ```{eval-rst}
-  .. table::
-     :align: left
-
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10   |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread1 | Entry | P1  | Q1  | R1  | S1  | P3  | Q3  | R3  | S3  | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread2 | Entry | P2  | Q2  |     | S2  | P4  | Q4  | R2  | S4  | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+------+
-  ```
+  :::{list-table}
+  :align: left
+
+  * -
+    - 1
+    - 2
+    - 3
+    - 4
+    - 5
+    - 6
+    - 7
+    - 8
+    - 9
+    - 10
+  * - Thread1
+    - Entry
+    - P1
+    - Q1
+    - R1
+    - S1
+    - P3
+    - Q3
+    - R3
+    - S3
+    - Exit
+  * - Thread2
+    - Entry
+    - P2
+    - Q2
+    -
+    - S2
+    - P4
+    - Q4
+    - R2
+    - S4
+    - Exit
+  :::
 
 > :::{note}
 > In general, the cycle `C` in the above statements is not
@@ -651,33 +820,98 @@ or `R` is the header.
 
 - Convergence when `P` is the header.
 
-  ```{eval-rst}
-  .. table::
-     :align: left
-
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11  | 12  | 13   |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread1 | Entry |     |     |     | P1  | Q1  | R1  | S1  | P3  | Q3  |     | S3  | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread2 | Entry |     | R2  | S2  | P2  | Q2  |     | S2  | P4  | Q4  | R3  | S4  | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-  ```
+  :::{list-table}
+  :align: left
+
+  * -
+    - 1
+    - 2
+    - 3
+    - 4
+    - 5
+    - 6
+    - 7
+    - 8
+    - 9
+    - 10
+    - 11
+    - 12
+    - 13
+  * - Thread1
+    - Entry
+    -
+    -
+    -
+    - P1
+    - Q1
+    - R1
+    - S1
+    - P3
+    - Q3
+    -
+    - S3
+    - Exit
+  * - Thread2
+    - Entry
+    -
+    - R2
+    - S2
+    - P2
+    - Q2
+    -
+    - S2
+    - P4
+    - Q4
+    - R3
+    - S4
+    - Exit
+  :::
 
 - Convergence when `R` is the header.
 
-  ```{eval-rst}
-  .. table::
-     :align: left
-
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     |         | 1     | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11  | 12   |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread1 | Entry |     | P1  | Q1  | R1  | S1  | P3  | Q3  | S3  |     |     | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-     | Thread2 | Entry |     |     |     | R2  | S2  | P2  | Q2  | S2  | P4  | ... | Exit |
-     +---------+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
-  ```
+  :::{list-table}
+  :align: left
+
+  * -
+    - 1
+    - 2
+    - 3
+    - 4
+    - 5
+    - 6
+    - 7
+    - 8
+    - 9
+    - 10
+    - 11
+    - 12
+  * - Thread1
+    - Entry
+    -
+    - P1
+    - Q1
+    - R1
+    - S1
+    - P3
+    - Q3
+    - S3
+    -
+    -
+    - Exit
+  * - Thread2
+    - Entry
+    -
+    -
+    -
+    - R2
+    - S2
+    - P2
+    - Q2
+    - S2
+    - P4
+    - ...
+    - Exit
+  :::
 
 Thus, when diverged paths reach different entries of an irreducible
 cycle from outside the cycle, the static analysis conservatively
diff --git a/llvm/docs/GwpAsan.md b/llvm/docs/GwpAsan.md
index 87b85845dc83b..85d27dc94ca68 100644
--- a/llvm/docs/GwpAsan.md
+++ b/llvm/docs/GwpAsan.md
@@ -153,33 +153,39 @@ extern "C" const char *__gwp_asan_default_options() {
 
 The following options are available:
 
-```{eval-rst}
-+----------------------------+---------+--------------------------------------------------------------------------------+
-| Option                     | Default | Description                                                                    |
-+----------------------------+---------+--------------------------------------------------------------------------------+
-| Enabled                    | true    | Is GWP-ASan enabled?                                                           |
-+----------------------------+---------+--------------------------------------------------------------------------------+
-| PerfectlyRightAlign        | false   | When allocations are right-aligned, should we perfectly align them up to the   |
-|                            |         | page boundary? By default (false), we round up allocation size to the nearest  |
-|                            |         | power of two (2, 4, 8, 16) up to a maximum of 16-byte alignment for            |
-|                            |         | performance reasons. Setting this to true can find single byte                 |
-|                            |         | buffer-overflows at the cost of performance, and may be incompatible with      |
-|                            |         | some architectures.                                                            |
-+----------------------------+---------+--------------------------------------------------------------------------------+
-| MaxSimultaneousAllocations | 16      | Number of simultaneously-guarded allocations available in the pool.            |
-+----------------------------+---------+--------------------------------------------------------------------------------+
-| SampleRate                 | 5000    | The probability (1 / SampleRate) that a page is selected for GWP-ASan          |
-|                            |         | sampling. Sample rates up to (2^31 - 1) are supported.                         |
-+----------------------------+---------+--------------------------------------------------------------------------------+
-| InstallSignalHandlers      | true    | Install GWP-ASan signal handlers for SIGSEGV during dynamic loading. This      |
-|                            |         | allows better error reports by providing stack traces for allocation and       |
-|                            |         | deallocation when reporting a memory error. GWP-ASan's signal handler will     |
-|                            |         | forward the signal to any previously-installed handler, and user programs      |
-|                            |         | that install further signal handlers should make sure they do the same. Note,  |
-|                            |         | if the previously installed SIGSEGV handler is SIG_IGN, we terminate the       |
-|                            |         | process after dumping the error report.                                        |
-+----------------------------+---------+--------------------------------------------------------------------------------+
-```
+:::{list-table}
+
+* - Option
+  - Default
+  - Description
+* - Enabled
+  - true
+  - Is GWP-ASan enabled?
+* - PerfectlyRightAlign
+  - false
+  - When allocations are right-aligned, should we perfectly align them up to the
+    page boundary? By default (false), we round up allocation size to the nearest
+    power of two (2, 4, 8, 16) up to a maximum of 16-byte alignment for
+    performance reasons. Setting this to true can find single byte
+    buffer-overflows at the cost of performance, and may be incompatible with
+    some architectures.
+* - MaxSimultaneousAllocations
+  - 16
+  - Number of simultaneously-guarded allocations available in the pool.
+* - SampleRate
+  - 5000
+  - The probability (1 / SampleRate) that a page is selected for GWP-ASan
+    sampling. Sample rates up to (2^31 - 1) are supported.
+* - InstallSignalHandlers
+  - true
+  - Install GWP-ASan signal handlers for SIGSEGV during dynamic loading. This
+    allows better error reports by providing stack traces for allocation and
+    deallocation when reporting a memory error. GWP-ASan's signal handler will
+    forward the signal to any previously-installed handler, and user programs
+    that install further signal handlers should make sure they do the same. Note,
+    if the previously installed SIGSEGV handler is SIG_IGN, we terminate the
+    process after dumping the error report.
+:::
 
 ### Example
 



More information about the llvm-branch-commits mailing list