Structure
A Jasmin program is a collection of:
requires;
parameters;
global variables;
types aliases;
functions.
Require
require <string>
require <string> ... <string>
from <ident> require <string> ... <string>
Since version 2022.04.0, a program may be split into several files using a
require
clause. Its simplest form is as follows
require "path/file.jinc"
Relative paths are resolved relative to the current file.
The required file is treated (mostly) as if its contents were put in
place of the require
clause.
TODO: More detail about “mostly”
Files can also be searched in named locations, for instance:
from AES require "aes.jinc"
This clause requires the file aes.jinc
that is located in a path named AES
.
There are two ways to give names to paths in the file-system:
either on the command-line using the argument
-I AES:actual/path/
;or using the
JASMINPATH
environment variable, as inexport JASMINPATH=AES=actual/path/
.
If several paths must be named, the -I
argument can be used multiple
times, and in the environment variable value,
several ID=path
pairs can be separated by a colon (:
).
Both require
clauses and from
can take several arguments, as in
require "path/file1.jinc" "path/file2.jinc" "path/file3.jinc"
and
from PATH require "file1.jinc" "file2.jinc" "file3.jinc"
Parameters
param <type> <ident> = <expr>;
A parameter is a named value. The name can be used within types (e.g., as the size of an array): as such, they can be used to provide a limited form of genericity.
Parameters are removed by the compiler right after parsing; in particular, they do not appear in the Coq abstract syntax. Therefore, they have no formal semantics.
A parameter is introduced by the param
keyword, followed by a type, a
name, and the value. For instance:
param int cROUNDS = 2;
Types Aliases
Jasmin compiler latest development version (still unreleased) introduced a new syntax feature for type definition. A type alias can be defined at top level of a program or a namespace. The aim of this feature is to improve genericity of Jasmin code. The types aliases are resolved like params, during parsing.
To define a type alias, we introduced the type
keyword, which is followed
by a name, and the a type. For example :
type reg_size = u64;
Defined types can then be used in programs in place of the compiler types.
reg reg_size a; //declaring variable a with type reg_size
reg reg_size[10] b; //declaring an array with elements of type reg_size
We can note that the feature doesn’t allow definitions like :
type x = reg u64;
We can consider that the storage type (reg
, stack
, inline
…) is
similar to the declaration keyword let
in other languages (for instance Rust).
Storage type is thus not a type, and should not be aliased.
Global variables
A global variable is a named valued. Unlike parameters, said value is not available at compile-time (i.e., it cannot be used as part of a type), but it is available at run-time.
Technically, global variables are stored in the code segment and are accessed to using PC-relative addressing mode.
There is no keyword to introduce a global variable declaration at the top-level, only its type, name, and value. For instance:
u128 rotate24pattern = (16u8)[ 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ];
In this example, the value is a 128-bit machine-word, described as a vector of 16 8-bit machine-words.
Functions
A function is introduced using the fn
keyword, followed by the function name,
the list of its parameters, its return type (if any), and its body.
The body begins with the declaration of local variables and ends with
a return
statement.
Here is an example function:
inline
fn shift(reg u128 x, inline int count) -> reg u128 {
reg u128 r;
r = #VPSLL_4u32(x, count);
return r;
}
export
fn add(reg u64 x, reg u64 y) -> reg u64 {
reg u64 r;
r = x;
r += y;
return r;
}
Function definitions can be prefixed with either the inline
or export
keywords to denote, respectively, that the function will be inlined by the
compiler or that it will respect a specified calling convention.
Since version 2022.04.0, functions may not be marked
with inline
or export
. Such functions are called local functions, and
they will be preserved by the compiler but are not visible from the outside
(i.e., they cannot be called from non-Jasmin code).
TODO: Add link to locality syntax.
Function definitions can also be prefixed by annotations. Annotations are key-value pairs that customize the behaviour of the compiler, for instance to store the return address of a function in a register or in the stack. For instance,
#[returnaddress=stack]
fn copy_to_memory(reg u64 x, reg u64 p) {
[p + 8] = x;
}
is a local function that copies the contents of a register x
to an address
(stored in a register p
) with an offset of 8, and takes its return address
from the stack.
TODO: Add link to annotation syntax.