Re: [PATCH -perfbook 1/3] Enhance \clnref for multi-part listing and employ it

"Paul E. McKenney" <[email protected]> Mon, 13 Jul 2026 09:43:25 -0700
Newsgroups org.kernel.vger.perfbook
Message-ID <6c84c284-efb5-4ec0-b842-dfb855f3b453@paulmck-laptop>
On Mon, Jul 13, 2026 at 06:52:41PM +0900, Akira Yokosawa wrote:
> Line counts in multi-part listings are printed in the form of "A1"
> and "B2".  While \clnref{} prints "line 1" and "line 2", rather
> than "line A1" and "line B2".
> 
> To make \clnref{} able to put prefix strings in line counts, define
> a macro "\lnpref" and add it in the definition of "\lnref{}".
> 
> For ease of setting \lnpref in the middle of a paragraph, provide
> \setlnpref{}, which prevents extra white spaces around it.
> "\setlnpref{A}" inside a "fcvref" env will set "A" to \lnpref.
> It may be used multiple times in a "fcvref" env for switching to
> other prefix(es).  It has no effect outside of said "fcvref" env.
> 
> \lnpref is not related to \myfvline of VerbatimT env.
> In other words, you need to do \setlnpref{} every time in
> front of \clnref{}.
> 
> Apply the new scheme in multi-part listings added up to commit
> 688d07e98e61 ("toolsoftrade: Wordsmith shenanigans QQ and add
> another").
> 
> Signed-off-by: Akira Yokosawa <[email protected]>
> ---
> Technically speaking, it should be possible to embed a prefix string
> along with each line label; and use it in \lnref{} without any need
> of \sellnpref{}.  This will be the ultimate goal!

Still a nice improvement, and thank you!  I need to apply this to my
recent changes, and will do so.  It might take me some time to get there.

Queued!!!

							Thanx, Paul

> --
>  defer/rcuintro.tex            | 105 +++++++++++++---------
>  perfbook-lt.tex               |   5 +-
>  toolsoftrade/toolsoftrade.tex | 158 +++++++++++++++++++++-------------
>  3 files changed, 169 insertions(+), 99 deletions(-)
> 
> diff --git a/defer/rcuintro.tex b/defer/rcuintro.tex
> index 46625c67..b9b7c8fc 100644
> --- a/defer/rcuintro.tex
> +++ b/defer/rcuintro.tex
> @@ -72,31 +72,36 @@ that would normally be used in single-threaded code.
>  \begin{adjustwidth}{10pt}{5pt}
>  \footnotesize
>  \renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> - p = gp;
> - do_something_with(p->a);
> - do_something_with(p->b);
> +\begin{fcvlabel}[ln:defer:Compilers Can Reload Values]
> +\begin{VerbatimT}[commandchars=\\\[\]]
> + p = gp;				\lnlbl[A1]
> + do_something_with(p->a);		\lnlbl[A2]
> + do_something_with(p->b);		\lnlbl[A3]
>  \end{VerbatimT}
>  Might be transformed to:
>  \renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> - p = gp;
> - do_something_with(p->a);
> - p = gp;
> - do_something_with(p->b);
> +\begin{VerbatimT}[commandchars=\\\[\]]
> + p = gp;				\lnlbl[B1]
> + do_something_with(p->a);		\lnlbl[B2]
> + p = gp;				\lnlbl[B3]
> + do_something_with(p->b);		\lnlbl[B4]
>  \end{VerbatimT}
>  The compiler assumes normal variables do not spontaneously change,
>  \co{do_something_with()} might use many machine registers, and this
>  transformation reduces register pressure.
> -But if some other thread changes \co{gp} between lines~B1 and~B3 of the
> +\begin{fcvref}[ln:defer:Compilers Can Reload Values]
> +\setlnpref{B}
> +But if some other thread changes \co{gp} between \clnref{B1,B3} of the
>  transformed code, the values of \co{p->a} and \co{p->b} will be mismatched.
> +\end{fcvref}
>  Prevent this by using \co{rcu_dereference()} as follows:
>  \renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> - p = rcu_dereference(gp);
> - do_something_with(p->a);
> - do_something_with(p->b);
> +\begin{VerbatimT}[commandchars=\\\[\]]
> + p = rcu_dereference(gp);		\lnlbl[C1]
> + do_something_with(p->a);		\lnlbl[C2]
> + do_something_with(p->b);		\lnlbl[C3]
>  \end{VerbatimT}
> +\end{fcvlabel}
>  \end{adjustwidth}
>  \caption{Compilers Can Reload Values}
>  \label{lst:defer:Compilers Can Reload Values}
> @@ -106,70 +111,90 @@ Prevent this by using \co{rcu_dereference()} as follows:
>  \begin{adjustwidth}{10pt}{5pt}
>  \footnotesize
>  \renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> - p = malloc(sizeof(*p));
> - p->a = compute_value();
> - p->b = 42;
> - gp = p;
> +\begin{fcvlabel}[ln:defer:Compilers Can Reorder Accesses]
> +\begin{VerbatimT}[commandchars=\\\[\]]
> + p = malloc(sizeof(*p));		\lnlbl[A1]
> + p->a = compute_value();		\lnlbl[A2]
> + p->b = 42;				\lnlbl[A3]
> + gp = p;				\lnlbl[A4]
>  \end{VerbatimT}
>  Might be transformed to:
>  \renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> - p = malloc(sizeof(*p));
> - gp = p;
> - p->a = compute_value();
> - p->b = 42;
> +\begin{VerbatimT}[commandchars=\\\[\]]
> + p = malloc(sizeof(*p));		\lnlbl[B1]
> + gp = p;				\lnlbl[B2]
> + p->a = compute_value();		\lnlbl[B3]
> + p->b = 42;				\lnlbl[B4]
>  \end{VerbatimT}
>  The compiler assumes normal variables are not concurrently accessed,
>  and thus that the order of stores does not matter.
>  If \co{compute_value()} was inlined, this transformation might produce
>  better code.
> +\begin{fcvref}[ln:defer:Compilers Can Reorder Accesses]
> +\setlnpref{B}
>  In this example, if some other thread loads \co{gp} immediately after
> -line~B2 of the transformed code, that thread might see pre-initialization
> -garbage in \co{p->a} and \co{p->b}.
> +\clnref{B2} of the transformed code, that thread might see
> +pre-initialization garbage in \co{p->a} and \co{p->b}.
> +\end{fcvref}
>  Prevent this by using \co{rcu_assign_pointer()} as follows:
>  \renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> - p = malloc(sizeof(*p));
> - p->a = compute_value();
> - p->b = 42;
> - rcu_assign_pointer(gp, p);
> +\begin{VerbatimT}[commandchars=\\\[\]]
> + p = malloc(sizeof(*p));		\lnlbl[C1]
> + p->a = compute_value();		\lnlbl[C2]
> + p->b = 42;				\lnlbl[C3]
> + rcu_assign_pointer(gp, p);		\lnlbl[C4]
>  \end{VerbatimT}
> +\end{fcvlabel}
>  \end{adjustwidth}
>  \caption{Compilers Can Reorder Accesses}
>  \label{lst:defer:Compilers Can Reorder Accesses}
>  \end{listing}
>  
> -To see the need for \co{rcu_dereference()}, please see lines~A1--A3 of
> +\begin{fcvref}[ln:defer:Compilers Can Reload Values]
> +\setlnpref{A}
> +To see the need for \co{rcu_dereference()}, please see
> +\clnrefrange{A1}{A3} of
>  \cref{lst:defer:Compilers Can Reload Values}.
>  Here, the compiler may assume that \co{gp} retains the same value
>  throughout.
> +\setlnpref{B}
>  If \co{do_something_with()} uses many machine registers, then the
> -compiler might transform this code to the form shown in lines~B1--B4 of
> +compiler might transform this code to the form shown in
> +\clnrefrange{B1}{B4} of
>  this listing, thus avoiding the need to dedicate a register to the value
>  loaded from \co{gp}, but also possibly passing values from two
>  different structures to the pair of calls to \co{do_something_with()}
> -on lines~B2 and~B4.
> +on \clnref{B2,B4}.
>  This transformation would fatally confuse any implementation of
>  \co{do_something_with()} that assumed that it was being passed values
>  from the same structure.
> +\setlnpref{C}
>  To prevent this transformation, use \co{rcu_dereference()} as
> -shown on lines~C1--C3 of this listing, thus informing the compiler of the
> +shown on \clnrefrange{C1}{C3} of this listing,
> +thus informing the compiler of the
>  possibility of concurrent updates to \co{gp}.
> +\end{fcvref}
>  
> -To see the need for \co{rcu_assign_pointer()}, please see lines~A1--A4 of
> +\begin{fcvref}[ln:defer:Compilers Can Reorder Accesses]
> +\setlnpref{A}
> +To see the need for \co{rcu_assign_pointer()}, please see
> +\clnrefrange{A1}{A4} of
>  \cref{lst:defer:Compilers Can Reorder Accesses},
>  in which the compiler may assume that \co{gp} is not concurrently accessed
>  at all.
>  If \co{compute_value()} is inlined, the compiler might generate better
> -code by pulling the assignment to \co{gp} from line~A4 to follow line~A1,
> -as shown in the transformed code on lines~B1--B4.
> +code by pulling the assignment to \co{gp} from
> +\clnref{A4} to follow \clnref{A1},
> +\setlnpref{B}
> +as shown in the transformed code on \clnrefrange{B1}{B4}.
>  If some other thread were to load fields \co{gp->a} or \co{gp->b} between
> -the store on line~B2 and the initializations on lines~B3 and~B4, that
> +the store on \clnref{B2} and the initializations on \clnref{B3,B4}, that
>  other thread's acceses would return pre-initialization garbage.
> +\setlnpref{C}
>  You can prevent this transformation by using \co{rcu_assign_pointer()}
> -as shown on lines~C1--C4, thus informing the compiler of the possibility
> +as shown on \clnrefrange{C1}{C4}, thus informing the compiler of the possibility
>  of concurrent accesses to \co{gp} and of the need to preserve ordering.
> +\end{fcvref}
>  
>  Reviewing \cref{fig:defer:Insertion With Concurrent Readers}
>  from the viewpoint of readers, in the first three states all readers
> diff --git a/perfbook-lt.tex b/perfbook-lt.tex
> index f6d6efc2..6f0b224f 100644
> --- a/perfbook-lt.tex
> +++ b/perfbook-lt.tex
> @@ -714,8 +714,11 @@
>  \renewcommand{\lnlbl}[1]{%
>  \raisebox{\lnlblraise}{\phantomsection\label{\lnlblbase:#1}}}%
>  }
> +
>  \newcommand{\lnrefbase}{}
> -\newcommand{\lnref}[1]{\ref{\lnrefbase:#1}}
> +\newcommand{\lnpref}{}
> +\newcommand{\setlnpref}[1]{\ignorespaces\renewcommand{\lnpref}{#1}\ignorespaces}
> +\newcommand{\lnref}[1]{\lnpref\ref{\lnrefbase:#1}}
>  \newcommand{\lnrefraw}[1]{\ref{#1}}
>  
>  \newenvironment{fcvlabel}[1][]{\renewcommand{\lnlblbase}{#1}%
> diff --git a/toolsoftrade/toolsoftrade.tex b/toolsoftrade/toolsoftrade.tex
> index 5ad57269..c864c484 100644
> --- a/toolsoftrade/toolsoftrade.tex
> +++ b/toolsoftrade/toolsoftrade.tex
> @@ -1488,32 +1488,37 @@ A short answer to this question is ``they lived dangerously''.
>  \begin{adjustwidth}{10pt}{5pt}
>  \footnotesize
>  \renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -ptr = global_ptr;
> -if (ptr != NULL && ptr < high_address)
> -	do_low(ptr);
> +\begin{fcvlabel}[ln:toolsoftrade:Living Dangerously Early 1990s Style]
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +ptr = global_ptr;			\lnlbl[A1]
> +if (ptr != NULL && ptr < high_address)	\lnlbl[A2]
> +	do_low(ptr);			\lnlbl[A3]
>  \end{VerbatimT}
>  Might be transformed to:
>  \renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -if (global_ptr != NULL &&
> -    global_ptr < high_address)
> -	do_low(global_ptr);
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +if (global_ptr != NULL &&		\lnlbl[B1]
> +    global_ptr < high_address)		\lnlbl[B2]
> +	do_low(global_ptr);		\lnlbl[B3]
>  \end{VerbatimT}
> +\begin{fcvref}[ln:toolsoftrade:Living Dangerously Early 1990s Style]
>  The compiler assumes normal variables do not spontaneously change,
>  the surrounding code  might use many machine registers, and this
>  transformation reduces register pressure.
> +\setlnpref{B}
>  But if some other thread changes \co{global_ptr} from non-\co{NULL}
> -to \co{NULL} between lines~B1 and~B2 of the transformed code, the
> +to \co{NULL} between \clnref{B1,B2} of the transformed code, the
>  two comparisons will be against different variables, possibly passing
>  \co{do_low} a \co{NULL} pointer.
>  Prevent this by using \co{READ_ONCE()} as follows:
> +\end{fcvref}
>  \renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -ptr = READ_ONCE(global_ptr);
> -if (ptr != NULL && ptr < high_address)
> -	do_low(ptr);
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +ptr = READ_ONCE(global_ptr);			\lnlbl[C1]
> +if (ptr != NULL && ptr < high_address)		\lnlbl[C2]
> +	do_low(ptr);				\lnlbl[C3]
>  \end{VerbatimT}
> +\end{fcvlabel}
>  \end{adjustwidth}
>  \caption{Living Dangerously Early 1990s Style}
>  \label{lst:toolsoftrade:Living Dangerously Early 1990s Style}
> @@ -1524,32 +1529,42 @@ present-day compilers.
>  In (say) the early 1990s, compilers did fewer optimizations, in part
>  because there were fewer compiler writers and in part due to the
>  relatively small memories of that era.
> -Nevertheless, problems did arise, as shown in lines~A1--A3 of
> +\begin{fcvref}[ln:toolsoftrade:Living Dangerously Early 1990s Style]
> +Nevertheless, problems did arise, as shown in
> +\setlnpref{A}
> +\clnrefrange{A1}{A3} of
>  \cref{lst:toolsoftrade:Living Dangerously Early 1990s Style},
> +\setlnpref{B}
>  which the compiler is within its rights to transform into
> -lines~B1--B3.
> -As you can see, the temporary on line~A1 has been optimized away, so
> +\clnrefrange{B1}{B3}.
> +\setlnpref{A}
> +As you can see, the temporary on \clnref{A1} has been optimized away, so
>  that \co{global_ptr} will be loaded up to three times.
> -This can be prevented using \co{READ_ONCE()} as shown on line~C1,
> +\setlnpref{C}
> +This can be prevented using \co{READ_ONCE()} as shown on \clnref{C1},
>  as is explained in detail in
>  \cref{sec:toolsoftrade:A Volatile Solution}.
> +\end{fcvref}
>  
>  \QuickQuiz{
>  	What is wrong with loading
>  	\cref{lst:toolsoftrade:Living Dangerously Early 1990s Style}'s
>  	\co{global_ptr} up to three times?
>  }\QuickQuizAnswer{
> +	\begin{fcvref}[ln:toolsoftrade:Living Dangerously Early 1990s Style]
> +	\setlnpref{B}
>  	Suppose that \co{global_ptr} is initially non-\co{NULL},
>  	but that some other thread sets \co{global_ptr} to \co{NULL}.
> -	Suppose further that line~B1 of the transformed code
> +	Suppose further that \clnref{B1} of the transformed code
>  	(\cref{lst:toolsoftrade:Living Dangerously Early 1990s Style})
>  	executes just before \co{global_ptr} is set to \co{NULL} and
> -	line~B2 just after.
> -	Then line~B1 will conclude that \co{global_ptr} is non-\co{NULL},
> -	and line~B2  will conclude that it is less than
> +	\clnref{B2} just after.
> +	Then \clnref{B1} will conclude that \co{global_ptr} is non-\co{NULL},
> +	and \clnref{B2}  will conclude that it is less than
>  	\co{high_address},
> -	so that line~B3 passes \co{do_low()} a \co{NULL} pointer,
> +	so that \clnref{B3} passes \co{do_low()} a \co{NULL} pointer,
>  	which \co{do_low()} just might not be prepared to deal with.
> +	\end{fcvref}
>  
>  	Your editor made exactly this mistake in the DYNIX/ptx
>  	kernel's memory allocator in the early 1990s.
> @@ -1598,10 +1613,13 @@ below.
>  \begin{description}[labelsep=.4em]
>  \item[Load tearing] occurs when the compiler uses multiple load
>  instructions for a single access.
> +\begin{fcvref}[ln:toolsoftrade:Living Dangerously Early 1990s Style]
> +\setlnpref{A}
>  For example, the compiler could in theory compile the load from
> -\co{global_ptr} (see line~A1 of
> +\co{global_ptr} (see \clnref{A1} of
>  \cref{lst:toolsoftrade:Living Dangerously Early 1990s Style})
>  as a series of one-byte loads.
> +\end{fcvref}
>  If some other thread was concurrently setting \co{global_ptr} to
>  \co{NULL}, the result might have all but one byte of the pointer
>  set to zero, thus forming a ``wild pointer''.
> @@ -1614,8 +1632,11 @@ a given pointer.
>  Because the C standard must support all manner of systems, the standard
>  cannot rule out load tearing in the general case.
>  
> -However, adding a \co{READ_ONCE()} as shown on line~C1 prevents load
> +\begin{fcvref}[ln:toolsoftrade:Living Dangerously Early 1990s Style]
> +\setlnpref{C}
> +However, adding a \co{READ_ONCE()} as shown on \clnref{C1} prevents load
>  tearing on modern systems in most situations.
> +\end{fcvref}
>  
>  \item[Store tearing] occurs when the compiler uses multiple store
>  instructions for a single access.
> @@ -1651,25 +1672,27 @@ prevent store tearing.
>  \begin{adjustwidth}{10pt}{5pt}
>  \footnotesize
>  \renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -while (!need_to_stop)
> -	do_something_quickly();
> +\begin{fcvlabel}[ln:toolsoftrade:Infinite Load Fusing]
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +while (!need_to_stop)			\lnlbl[A1]
> +	do_something_quickly();		\lnlbl[A2]
>  \end{VerbatimT}
>  Might be transformed to:
>  \renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -if (!need_to_stop)
> -	for (;;)
> -		do_something_quickly();
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +if (!need_to_stop)			\lnlbl[B1]
> +	for (;;)			\lnlbl[B2]
> +		do_something_quickly();	\lnlbl[B3]
>  \end{VerbatimT}
>  The compiler assumes normal variables do not spontaneously change,
>  and might thus load \co{need_to_stop} only the one time.
>  Prevent this by using \co{READ_ONCE()} as follows:
>  \renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -while (!READ_ONCE(need_to_stop))
> -	do_something_quickly();
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +while (!READ_ONCE(need_to_stop))	\lnlbl[C1]
> +	do_something_quickly();		\lnlbl[C2]
>  \end{VerbatimT}
> +\end{fcvlabel}
>  \end{adjustwidth}
>  \caption{Infinite Load Fusing}
>  \label{lst:toolsoftrade:Infinite Load Fusing}
> @@ -1700,37 +1723,43 @@ For example, suppose that it is necessary to invoke a function
>  named \co{do_something_quickly()} repeatedly until the variable
>  \co{need_to_stop} was set, and that the compiler can see that
>  \co{do_something_quickly()} does not store to \co{need_to_stop}.
> -One (unsafe) way to code this is shown on lines~A1--A2 of
> +\begin{fcvref}[ln:toolsoftrade:Infinite Load Fusing]
> +\setlnpref{A}
> +One (unsafe) way to code this is shown on \clnrefrange{A1}{A2} of
>  \cref{lst:toolsoftrade:Infinite Load Fusing}.
> -Because line~A1 does a plain load and because the compiler knows that
> +Because \clnref{A1} does a plain load and because the compiler knows that
>  \co{do_something_quickly()} does not store to \co{need_to_stop}, the
>  compiler could quite reasonably decide to check this variable only once,
> -resulting in the code shown on lines~B1--B3 of the listing.
> +\setlnpref{B}
> +resulting in the code shown on \clnrefrange{B1}{B3} of the listing.
>  Once entered, the loop on will never exit, regardless of how
>  many times some other thread stores a non-zero value to \co{need_to_stop}.
>  The result will at best be consternation, and might well also entail
>  severe physical damage.
> +\setlnpref{C}
>  You can prevent such disasters by using \co{READ_ONCE()} as shown
> -on line~C1.
> +on \clnref{C1}.
> +\end{fcvref}
>  
>  \begin{listing}[tb]
>  \begin{adjustwidth}{10pt}{5pt}
>  \footnotesize
> +\begin{fcvlabel}[ln:toolsoftrade:Finite Load Fusing]
>  \renewcommand{\myfvline}{A\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -while (!nonvolatile_atomic_load(need_to_stop))
> -	do_something_quickly();
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +while (!nonvolatile_atomic_load(need_to_stop))  \lnlbl[A1]
> +	do_something_quickly();			\lnlbl[A2]
>  \end{VerbatimT}
>  Might be transformed to:
>  \renewcommand{\myfvline}{B\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -if (!nonvolatile_atomic_load(need_to_stop))
> -	for (;;) {
> -		do_something_quickly();
> -		do_something_quickly();
> -		do_something_quickly();
> -		do_something_quickly();
> -	}
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +if (!nonvolatile_atomic_load(need_to_stop))	\lnlbl[B1]
> +	for (;;) {				\lnlbl[B2]
> +		do_something_quickly();		\lnlbl[B3]
> +		do_something_quickly();		\lnlbl[B4]
> +		do_something_quickly();		\lnlbl[B5]
> +		do_something_quickly();		\lnlbl[B6]
> +	}					\lnlbl[B7]
>  \end{VerbatimT}
>  In the absence of ordering in \co{do_something_quickly()}, the compiler is
>  permitted to do finite load fusing, in this case combining four adjacent
> @@ -1740,10 +1769,11 @@ might result in a failure to meet loop-exit latency constraints after
>  some other thread stores to \co{need_to_stop}.
>  Prevent this by using \co{READ_ONCE()} as follows:
>  \renewcommand{\myfvline}{C\arabic{FancyVerbLine}}
> -\begin{VerbatimT}
> -while (!READ_ONCE(need_to_stop))
> -	do_something_quickly();
> +\begin{VerbatimT}[commandchars=\\\[\]]
> +while (!READ_ONCE(need_to_stop))	\lnlbl[C1]
> +	do_something_quickly();		\lnlbl[C2]
>  \end{VerbatimT}
> +\end{fcvlabel}
>  \end{adjustwidth}
>  \caption{Finite Load Fusing}
>  \label{lst:toolsoftrade:Finite Load Fusing}
> @@ -1754,14 +1784,17 @@ language's non-volatile atomics, which often provide a nice compromise
>  between reasonable guarantees and decent optimization.
>  These optimizations can be especially effective when
>  \co{do_something_quickly()} is an inline function.
> +\begin{fcvref}[ln:toolsoftrade:Finite Load Fusing]
> +\setlnpref{A}
>  Because the C language provides only volatile atomic operations,
> -line~A1 of
> +\clnref{A1} of
>  \cref{lst:toolsoftrade:Finite Load Fusing}
>  indicates this with the mythical \co{nonvolatile_atomic_load()} operation.
>  
> +\setlnpref{B}
>  However, those working on real-time code must take care because finite
>  load fusing is permitted, which can lead to finite loop unrolling,
> -as as exemplified by the four-way unrolling on lines~B1-B7 of
> +as as exemplified by the four-way unrolling on \clnrefrange{B1}{B7} of
>  the listing.
>  Although this loop unrolling does not cause a functional failure, the
>  unrolled loop might cause the \co{do_something_quickly()} function to be
> @@ -1770,8 +1803,10 @@ a non-zero value to \co{need_to_stop} and the time that the loop exits.
>  These additional executions of \co{do_something_quickly()} might result
>  in failures to meet loop-exit latency constraints, especially given that
>  the value of ``finite'' can be extremely large.
> +\setlnpref{C}
>  Prevent this latency-degrading unrolling by using \co{READ_ONCE()}
> -as shown on line~C1.
> +as shown on \clnref{C1}.
> +\end{fcvref}
>  
>  \QuickQuiz{
>  	Why does it matter whether \co{do_something_quickly()} in
> @@ -1783,11 +1818,14 @@ as shown on line~C1.
>  	would have to assume that this function might change the value
>  	of \co{need_to_stop}.  Or that this function might include
>  	ordering constraints and accesses to other shared variables.
> +	\begin{fcvref}[ln:toolsoftrade:Finite Load Fusing]
> +	\setlnpref{A}
>  	These possibilities would force the compiler to reload
> -	\co{need_to_stop} on each pass through the loop on lines~A1--A2,
> +	\co{need_to_stop} on each pass through the loop on \clnrefrange{A1}{A2},
>  	thus preventing the compiler from unrolling the loop, or,
>  	alternatively, from omitting the load and test when unrolling
>  	the loop.
> +	\end{fcvref}
>  	The compiler's ignorance, coupled with the fact that the compiler
>  	understood that it was ignorant, would force the per-iteration
>  	reload from \co{need_to_stop}, which would in turn prevent the
> @@ -2268,10 +2306,14 @@ complex, and are left aside for the time being.
>  
>  So how does \apic{volatile} stack up against the earlier examples?
>  
> -Using \apik{READ_ONCE()} on line~A1 of
> +\begin{fcvref}[ln:toolsoftrade:Living Dangerously Early 1990s Style]
> +\setlnpref{A}
> +Using \apik{READ_ONCE()} on \clnref{A1} of
>  \cref{lst:toolsoftrade:Living Dangerously Early 1990s Style}
>  avoids invented loads,
> -resulting in the code shown on lines~C1--C3.
> +\setlnpref{C}
> +resulting in the code shown on \clnrefrange{C1}{C3}.
> +\end{fcvref}
>  
>  \begin{listing}
>  \begin{fcvlabel}[ln:toolsoftrade:Preventing Load Fusing]
> 
> base-commit: 5fa9e33570b14bc460ae59c3177563478ba93d75
> -- 
> 2.43.0
>