\section{HeapLang}
\label{sec:heaplang}

So far, we have treated the programming language we work on entirely generically.
In this section we present the default language that ships with Iris, HeapLang.
HeapLang is an ML-like languages with a higher-order heap, unstructured concurrency, some common atomic operations, and prophecy variables.
It is an instance of the language interface (\Sref{sec:language}), so we only define a per-thread small-step operational semantics---the thread-pool semantics are given in in \Sref{sec:language:concurrent}.

\subsection{HeapLang syntax and operational semantics}

The grammar of HeapLang, and in particular its set \Expr{} of \emph{expressions} and \Val{} of \emph{values}, is defined as follows:
\begin{align*}
\val,\valB \in \Val \bnfdef{}&
  z \mid
  \True \mid \False \mid
  \TT \mid
  \poison \mid
  \loc \mid
  \prophid \mid {}& (z \in \integer, \loc \in \Loc, \prophid \in \ProphId) \\&
  \RecV\lvarF(\lvar)= \expr \mid
  (\val,\valB)_\valForm \mid
  \InlV(\val) \mid
  \InrV(\val)  \\
\expr \in \Expr \bnfdef{}&
  \val \mid
  \lvar \mid
  \RecE\lvarF(\lvar)= \expr \mid
  \expr_1(\expr_2) \mid
  {}\\ &
  \HLOp_1 \expr \mid
  \expr_1 \HLOp_2 \expr_2 \mid
  \If \expr then \expr_1 \Else \expr_2 \mid
  {}\\ &
  (\expr_1,\expr_2)_\exprForm \mid
  \Fst(\expr) \mid
  \Snd(\expr) \mid
  {}\\ &
  \InlE(\expr) \mid
  \InrE(\expr) \mid
  \Match \expr with \Inl => \expr_1 | \Inr => \expr_2 end \mid
  {}\\ &
  \AllocN(\expr_1,\expr_2) \mid
  \Free(\expr) \mid
  \deref \expr \mid
  \expr_1 \gets \expr_2 \mid
  \CmpXchg(\expr_1, \expr_2, \expr_3) \mid
  \Xchg(\expr_1, \expr_2) \mid
  \FAA(\expr_1, \expr_2) \mid
  \kern-30ex{}\\ &
  \Fork \expr \mid
  \NewProph \mid
  \ResolveWith \expr_1 at \expr_2 to \expr_3 \\
\HLOp_1 \bnfdef{}& - \mid \ldots ~~\text{(list incomplete)} \\
\HLOp_2 \bnfdef{}& + \mid - \mid \Ptradd \mid \mathop{=} \mid \ldots ~~\text{(list incomplete)}
\end{align*}
(Note that \langkw{match} contains a literal $|$ that is not part of the BNF but part of HeapLang syntax.)

To simplify the formalization, the only binders occur in \langkw{rec}.
\langkw{match} has a closure in each arm which will be applied to the value of the left/right variant, respectively.
(See the syntactic sugar defined later.)

Recursive abstractions, pairs, and the injections exist both as a value form and an expression form.
The expression forms will reduce to the value form once all their arguments are values.
Conceptually, one can think of that as corresponding to ``boxing'' that most functional language implementations do.
We will leave away the disambiguating subscript when it is clear from the context or does not matter.
All of this lets us define $\ofval$ as simply applying the value injection (the very first syntactic form of $\Expr$), which makes a lot of things in Coq much simpler.
$\toval$ is defined recursively in the obvious way.

\langkw{AllocN} takes as first argument the number of heap cells to allocate (must be strictly positive), and as second argument the default value to use for these heap cells.
This lets one allocate arrays.
$\Ptradd$ implements pointer arithmetic (the left operand must be a pointer, the right operand an integer), which is used to access array elements.

For our set of states and observations, we pick
\begin{align*}
  \loc \ni \Loc \eqdef{}& \integer \\
  \prophid \ni \ProphId \eqdef{}& \integer \\
  \sigma \ni \State \eqdef{}& \record{\begin{aligned}
                   \stateHeap:{}& \Loc \fpfn \Val,\\
                   \stateProphs:{}& \pset{\ProphId}
                 \end{aligned}} \\
  \obs \ni \Obs \eqdef{}& \ProphId \times (\Val \times \Val)
\end{align*}

The HeapLang operational semantics is defined via the use of \emph{evaluation contexts}:
\begin{align*}
\lctx \in \Lctx \bnfdef{}&
  \bullet \mid \Lctx_{>} \\
\lctx_{>} \in \Lctx_{>} \bnfdef{}&
  \expr(\lctx) \mid
  \lctx (\val) \mid
  {}\\ &
  \HLOp_1 \lctx \mid
  \expr \HLOp_2 \lctx \mid
  \lctx \HLOp_2 \val \mid
  \If \lctx then \expr_1 \Else \expr_2 \mid
  {}\\ &
  (\expr, \lctx) \mid
  (\lctx, \val) \mid
  \Fst(\lctx) \mid
  \Snd(\lctx) \mid
  {}\\ &
  \Inl(\lctx) \mid
  \Inr(\lctx) \mid
  \Match \lctx with \Inl => \expr_1 | \Inr => \expr_2 end \mid
  {}\\ &
  \AllocN(\expr, \lctx) \mid
  \AllocN(\lctx, \val) \mid
  \Free(\lctx) \mid
  \deref \lctx \mid
  \expr \gets \lctx \mid
  \lctx \gets \val \mid
  {}\\ &
  \CmpXchg(\expr_1, \expr_2, \lctx) \mid
  \CmpXchg(\expr_1, \lctx, \val_3) \mid
  \CmpXchg(\lctx, \val_2, \val_3) \mid
  {}\\ &
  \Xchg(\expr, \lctx) \mid
  \Xchg(\lctx, \val) \mid
  \FAA(\expr, \lctx) \mid
  \FAA(\lctx, \val) \mid
  {}\\ &
  \ResolveWith \expr_1 at \expr_2 to \lctx \mid
  \ResolveWith \expr_1 at \lctx to \val_3 \mid
  {}\\ &
  \ResolveWith \lctx_{>} at \val_2 to \val_3
\end{align*}
Note that we use right-to-left evaluation order.
This means in a curried function call $f(x)(y)$, we know syntactically the arguments will all evaluate before $f$ gets to do anything, which makes specifying curried calls a lot easier.

The \langkw{resolve} evaluation context for the leftmost expression (the nested expression that executes atomically together with the prophecy resolution) is special: it must not be empty; only further nested evaluation contexts are allowed.
\langkw{resolve} takes care of reducing the expression once the nested contexts are taken care of, and at that point it requires the expression to reduce to a value in exactly one step.
Hence we define $\Lctx_{>}$ for non-empty evaluation contexts.
For more details on prophecy variables, see \cite{iris:prophecy}.

This lets us define the primitive reduction relation in terms of a ``head step'' reduction; see \figref{fig:heaplang-reduction-pure} and \figref{fig:heaplang-reduction-impure}.
Comparison (both for $\CmpXchg$ and the binary comparison operator) is a bit tricky and uses a helper judgment (\figref{fig:heaplang-valeq}).
Basically, two values can only be compared if at least one of them is ``compare-safe''.
Compare-safe values are basic literals (integers, Booleans, locations, unit) as well as $\Inl$ and $\Inr$ of those literals.
The intention of this is to forbid directly comparing large values such as pairs, which could not be done in a single atomic step on a real machine.

\begin{figure}[p]
\judgment[Per-thread reduction]{\expr_1, \pstate_1 \step [\vec\obs] \expr_2, \pstate_2, \vec\expr}
\begin{mathpar}
\infer
  {\expr_1, \pstate_1 \hstep [\vec\obs] \expr_2, \pstate_2, \vec\expr}
  {\fillctx\lctx[\expr_1], \pstate_1 \step[\vec\obs] \fillctx\lctx[\expr_2], \pstate_2, \vec\expr}
\end{mathpar}

\judgment[``Head'' reduction (pure)]{\expr_1, \pstate_1 \hstep [\vec\obs] \expr_2, \pstate_2, \vec\expr}
\newcommand\alignheader{\kern-30ex}
\begin{align*}
&\alignheader\textbf{``Boxing'' reductions} \\
(\RecE\lvarF(\lvar)= \expr, \pstate) \hstep[\nil]{}&
  (\RecV\lvarF(\lvar)= \expr, \pstate, \nil) \\
((\val_1, \val_2)_\exprForm, \pstate) \hstep[\nil]{}&
  ((\val_1, \val_2)_\valForm, \pstate, \nil) \\
(\InlE(\val), \pstate) \hstep[\nil]{}&
  (\InlV(\val), \pstate, \nil) \\
(\InrE(\val), \pstate) \hstep[\nil]{}&
  (\InrV(\val), \pstate, \nil) \\
&\alignheader\textbf{Pure reductions} \\
((\RecV\lvarF(\lvar)= \expr)(\val), \pstate) \hstep[\nil]{}&
  (\subst {\subst \expr \lvarF {(\Rec\lvarF(\lvar)= \expr)}} \lvar \val, \pstate, \nil) \\
(-_{\HLOp} z, \pstate) \hstep[\nil]{}&
  (-z, \pstate, \nil) \\
(z_1 +_{\HLOp} z_2, \pstate) \hstep[\nil]{}&
  (z_1 + z_2, \pstate, \nil) \\
(z_1 -_{\HLOp} z_2, \pstate) \hstep[\nil]{}&
  (z_1 - z_2, \pstate, \nil) \\
(\loc \Ptradd z, \pstate) \hstep[\nil]{}&
  (\loc + z, \pstate, \nil) \\
(\val_1 =_{\HLOp} \val_2, \pstate) \hstep[\nil]{}&
  (\True, \pstate, \nil)
  &&\text{if $\val_1 \valeq \val_2$} \\
(\val_1 =_{\HLOp} \val_2, \pstate) \hstep[\nil]{}&
  (\False, \pstate, \nil)
  &&\text{if $\val_1 \valne \val_2$} \\
(\If \True then \expr_1 \Else \expr_2, \pstate) \hstep[\nil]{}&
  (\expr_1, \pstate, \nil) \\
(\If \False then \expr_1 \Else \expr_2, \pstate) \hstep[\nil]{}&
  (\expr_2, \pstate, \nil) \\
(\Fst((\val_1, \val_2)_\valForm), \pstate) \hstep[\nil]{}&
  (\val_1, \pstate, \nil) \\
(\Snd((\val_1, \val_2)_\valForm), \pstate) \hstep[\nil]{}&
  (\val_2, \pstate, \nil) \\
(\Match \InlV(\val) with \Inl => \expr_1 | \Inr => \expr_2 end, \pstate) \hstep[\nil]{}&
  (\expr_1(\val), \pstate, \nil) \\
(\Match \InrV(\val) with \Inl => \expr_1 | \Inr => \expr_2 end, \pstate) \hstep[\nil]{}&
  (\expr_2(\val), \pstate, \nil)
\end{align*}
\caption{HeapLang pure and boxed reduction rules. \\ \small
The $\HLOp$ subscript indicates that this is the HeapLang operator, not the mathematical operator.}
\label{fig:heaplang-reduction-pure}
\end{figure}

\begin{figure}
\judgment[``Head'' reduction (impure)]{\expr_1, \pstate_1 \hstep [\vec\obs] \expr_2, \pstate_2, \vec\expr}
\newcommand\alignheader{\kern-30ex}
\begin{align*}
&\alignheader\textbf{Heap reductions} \\
(\AllocN(z, \val), \pstate) \hstep[\nil]{}&
  (\loc, \mapinsert {[\loc,\loc+z)} \val {\pstate:\stateHeap}, \nil)
  &&\text{if $z>0$ and \(\All i<z. \pstate.\stateHeap(\loc+i) = \bot\)} \\
(\Free(\loc), \pstate) \hstep[\nil]{}&
  ((), \mapinsert \loc \bot {\pstate:\stateHeap}, \nil) &&\text{if $\pstate.\stateHeap(l) = \val$} \\
(\deref\loc, \pstate) \hstep[\nil]{}&
  (\val, \pstate, \nil) &&\text{if $\pstate.\stateHeap(l) = \val$} \\
(\loc\gets\valB, \pstate) \hstep[\nil]{}&
  (\TT, \mapinsert \loc \valB {\pstate:\stateHeap}, \nil)  &&\text{if $\pstate.\stateHeap(l) = \val$} \\
(\CmpXchg(\loc,\valB_1,\valB_2), \pstate) \hstep[\nil]{}&
  ((\val, \True), \mapinsert\loc{\valB_2}{\pstate:\stateHeap}, \nil)
  &&\text{if $\pstate.\stateHeap(l) = \val$ and $\val \valeq \valB_1$} \\
(\CmpXchg(\loc,\valB_1,\valB_2), \pstate) \hstep[\nil]{}&
  ((\val, \False), \pstate, \nil)
  &&\text{if $\pstate.\stateHeap(l) = \val$ and $\val \valne \valB_1$} \\
(\Xchg(\loc, \valB) \hstep[\nil]{}&
  (\val, \mapinsert \loc \valB {\pstate:\stateHeap}, \nil) &&\text{if $\pstate.\stateHeap(l) = \val$} \\
(\FAA(\loc, z_2) \hstep[\nil]{}&
  (z_1, \mapinsert \loc {z_1+z_2} {\pstate:\stateHeap}, \nil) &&\text{if $\pstate.\stateHeap(l) = z_1$} \\
&\alignheader\textbf{Special reductions} \\
(\Fork\expr, \pstate) \hstep[\nil]{}&
  (\TT, \pstate, \expr) \\
(\NewProph, \pstate) \hstep[\nil]{}&
  (\prophid, \pstate:\stateProphs \uplus \set{\prophid}, \nil)
  &&\text{if $\prophid \notin \pstate.\stateProphs$}
\end{align*}
\begin{mathpar}
\infer
  {(\expr, \pstate) \hstep[\vec\obs] (\val, \pstate', \vec\expr')}
  {(\ResolveWith \expr at \prophid to \valB, \pstate) \hstep[\vec\obs \dplus [(\prophid, (\val, \valB))]] (\val, \pstate', \vec\expr')}
\end{mathpar}
\caption{HeapLang impure reduction rules. \\ \small
Here, $\pstate:\stateHeap$ denotes $\sigma$ with the $\stateHeap$ field updated as indicated.
$[\loc,\loc+z)$ in the $\AllocN$ rule indicates that we update all locations in this (left-closed, right-open) interval.}
\label{fig:heaplang-reduction-impure}
\end{figure}

\begin{figure}
\judgment[Value (in)equality]{\val \valeq \valB, \val \valne \valB}
\begin{align*}
  \val \valeq \valB \eqdef{}& \val = \valB \land (\valCompareSafe(\val) \lor \valCompareSafe(\valB)) \\
  \val \valne \valB \eqdef{}& \val \ne \valB \land (\valCompareSafe(\val) \lor \valCompareSafe(\valB))
\end{align*}
\begin{mathpar}
  \axiom{\litCompareSafe(z)}

  \axiom{\litCompareSafe(\True)}

  \axiom{\litCompareSafe(\False)}

  \axiom{\litCompareSafe(\loc)}

  \axiom{\litCompareSafe(\TT)}

\\

  \infer
    {\litCompareSafe(\val)}
    {\valCompareSafe(\val)}

  \infer
    {\litCompareSafe(\val)}
    {\valCompareSafe(\Inl(\val))}

  \infer
    {\litCompareSafe(\val)}
    {\valCompareSafe(\Inr(\val))}
\end{mathpar}
\caption{HeapLang value comparison judgment.}
\label{fig:heaplang-valeq}
\end{figure}

\subsection{Syntactic sugar}

We recover many of the common language operations as syntactic sugar.
\begin{align*}
  \Lam \lvar. \expr \eqdef{}& \Rec\any(\lvar)= \expr \\
  \Let \lvar = \expr in \expr' \eqdef{}& (\Lam \lvar. \expr')(\expr) \\
  \expr; \expr' \eqdef{}& \Let \any = \expr in \expr' \\
  \Skip \eqdef{}& \TT; \TT \\
  \expr_1 \mathop{\&\&} \expr_2 \eqdef{}& \If \expr_1 then \expr_2 \Else \False \\
  \expr_1 \mathop{||} \expr_2 \eqdef{}& \If \expr_1 then \True \Else \expr_2 \\
  \Match \expr with \Inl(\lvar) => \expr_1 | \Inr(\lvarB) => \expr_2 end \eqdef {}&
    \Match \expr with \Inl => \Lam\lvar. \expr_1 | \Inr => \Lam\lvarB. \expr_2 end \\
  \Alloc(\expr) \eqdef{}& \AllocN(1,\expr) \\
  \CAS(\expr_1, \expr_2, \expr_3) \eqdef{}& \Snd(\CmpXchg(\expr_1, \expr_2, \expr_3)) \\
  \Resolve \expr_1 to \expr_2 \eqdef{}& \ResolveWith \Skip at \expr_1 to \expr_2
\end{align*}

%%% Local Variables:
%%% mode: latex
%%% TeX-master: "iris"
%%% End:
