Theory Uint_Userguide
chapter ‹User guide for native words›
theory Uint_Userguide imports
Uint32
Uint16
Code_Target_Int_Bit
begin
text ‹
This tutorial explains how to best use the types for native
words like @{typ "uint32"} in your formalisation.
You can base your formalisation
\begin{enumerate}
\item either directly on these types,
\item or on the generic @{typ "'a word"} and only introduce native
words a posteriori via code generator refinement.
\end{enumerate}
The first option causes the least overhead if you have to prove only
little about the words you use and start a fresh formalisation.
Just use the native type @{typ uint32} instead of @{typ "32 word"}
and similarly for ‹uint64›, ‹uint16›, and ‹uint8›.
As native word types are meant only for code generation, the lemmas
about @{typ "'a word"} have not been duplicated, but you can transfer
theorems between native word types and @{typ "'a word"} using the
transfer package.
Note, however, that this option restricts your work a bit:
your own functions cannot be ``polymorphic'' in the word length,
but you have to define a separate function for every word length you need.
The second option is recommended if you already have a formalisation
based on @{typ "'a word"} or if your proofs involve words and their
properties. It separates code generation from modelling and proving,
i.e., you can work with words as usual. Consequently, you have to
manually setup the code generator to use the native types wherever
you want. The following describes how to achieve this with moderate
effort.
Note, however, that some target languages of the code generator
(especially OCaml) do not support all the native word types provided.
Therefore, you should only import those types that you need -- the
theory file for each type mentions at the top the restrictions for
code generation. For example, PolyML does not provide the Word16
structure, and OCaml provides neither Word8 nor Word16.
You can still use these theories provided that you also import
the theory @{theory Native_Word.Code_Target_Int_Bit} (which implements
@{typ int} by target-language integers), but these words will
be implemented via Isabelle's ‹Word› library, i.e.,
you do not gain anything in terms of efficiency.
\textbf{There is a separate code target ‹SML_word› for SML.}
If you use one of the native words that PolyML does not support
(such as ‹uint16› and ‹uint64› in 32-bit mode), but would
like to map its operations to the Standard Basis Library functions,
make sure to use the target ‹SML_word› instead of ‹SML›;
if you only use native word sizes that PolyML supports, you can stick
with ‹SML›. This ensures that code generation within Isabelle
as used by ‹Quickcheck›, ‹value› and @\{code\} in ML blocks
continues to work.
›
section ‹Lifting functions from @{typ "'a word"} to native words›
text ‹
This section shows how to convert functions from @{typ "'a word"} to native
words. For example, the following function ‹sum_squares› computes
the sum of the first @{term n} square numbers in 16 bit arithmetic using
a tail-recursive function ‹gen_sum_squares› with accumulator;
for convenience, ‹sum_squares_int› takes an integer instead of a word.
›
function gen_sum_squares :: "16 word ⇒ 16 word ⇒ 16 word" where [simp del]:
"gen_sum_squares accum n =
(if n = 0 then accum else gen_sum_squares (accum + n * n) (n - 1))"
by pat_completeness simp
termination by (relation ‹measure (nat ∘ uint ∘ snd)›) (simp_all add: measure_unat)
definition sum_squares :: "16 word ⇒ 16 word" where
"sum_squares = gen_sum_squares 0"
definition sum_squares_int :: "int ⇒ 16 word" where
"sum_squares_int n = sum_squares (word_of_int n)"
text ‹
The generated code for @{term sum_squares} and @{term sum_squares_int}
emulates words with unbounded integers and explicit modulus as specified
in the theory @{theory "HOL-Library.Word"}. But for efficiency, we want that the
generated code uses machine words and machine arithmetic. Unfortunately,
as @{typ "'a word"} is polymorphic in the word length, the code generator
can only do this if we use another type for machine words. The theory
@{theory Native_Word.Uint16} defines the type @{typ uint16} for machine words of
16~bits. We just have to follow two steps to use it:
First, we lift all our functions from @{typ "16 word"} to @{typ uint16},
i.e., @{term sum_squares}, @{term gen_sum_squares}, and
@{term sum_squares_int} in our case. The theory @{theory Native_Word.Uint16} sets
up the lifting package for this and has already taken care of the
arithmetic and bit-wise operations.
›
lift_definition gen_sum_squares_uint :: "uint16 ⇒ uint16 ⇒ uint16"
is gen_sum_squares .
lift_definition sum_squares_uint :: "uint16 ⇒ uint16" is sum_squares .
lift_definition sum_squares_int_uint :: "int ⇒ uint16" is sum_squares_int .
text ‹
Second, we also have to transfer the code equations for our functions.
The attribute ‹Transfer.transferred› takes care of that, but it is
better to check that the transfer succeeded: inspect the theorem to check
that the new constants are used throughout.
›
lemmas [Transfer.transferred, code] =
gen_sum_squares.simps
sum_squares_def
sum_squares_int_def
text ‹
Finally, we export the code to standard ML. We use the target
‹SML_word› instead of ‹SML› to have the operations
on @{typ uint16} mapped to the Standard Basis Library. As PolyML
does not provide a Word16 type, the mapping for @{typ uint16} is only
active in the refined target ‹SML_word›.
›
export_code sum_squares_int_uint in SML_word
text ‹
Nevertheless, we can still evaluate terms with @{term "uint16"} within
Isabelle, i.e., PolyML, but this will be translated to @{typ "16 word"}
and therefore less efficient.
›
value "sum_squares_int_uint 40"
section ‹Storing native words in datatypes›
text ‹
The above lifting is necessary for all functions whose type mentions
the word type. Fortunately, we do not have to duplicate functions that
merely operate on datatypes that contain words. Nevertheless, we have
to tell the code generator that these functions should call the new ones,
which operate on machine words. This section shows how to achieve this
with data refinement.
›
subsection ‹Example: expressions and two semantics›
text ‹
As the running example, we consider a language of expressions (literal values, less-than comparisions and conditional) where values are either booleans or 32-bit words.
The original specification uses the type @{typ "32 word"}.
›