You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
887 lines
29 KiB
887 lines
29 KiB
***************************
|
|
Cretonne Language Reference
|
|
***************************
|
|
|
|
.. default-domain:: cton
|
|
.. highlight:: cton
|
|
|
|
The Cretonne intermediate language has two equivalent representations: an
|
|
*in-memory data structure* that the code generator library is using, and
|
|
a *text format* which is used for test cases and debug output. Files containing
|
|
Cretonne textual IL have the ``.cton`` filename extension.
|
|
|
|
This reference uses the text format to describe IL semantics but glosses over
|
|
the finer details of the lexical and syntactic structure of the format.
|
|
|
|
|
|
Overall structure
|
|
=================
|
|
|
|
Cretonne compiles functions independently. A ``.cton`` IL file may contain
|
|
multiple functions, and the programmatic API can create multiple function
|
|
handles at the same time, but the functions don't share any data or reference
|
|
each other directly.
|
|
|
|
This is a simple C function that computes the average of an array of floats:
|
|
|
|
.. literalinclude:: example.c
|
|
:language: c
|
|
|
|
Here is the same function compiled into Cretonne IL:
|
|
|
|
.. literalinclude:: example.cton
|
|
:language: cton
|
|
:linenos:
|
|
:emphasize-lines: 2
|
|
|
|
The first line of a function definition provides the function *name* and
|
|
the :term:`function signature` which declares the argument and return types.
|
|
Then follows the :term:`function preamble` which declares a number of entities
|
|
that can be referenced inside the function. In the example above, the preamble
|
|
declares a single local variable, ``ss1``.
|
|
|
|
After the preamble follows the :term:`function body` which consists of
|
|
:term:`extended basic block`\s, one of which is marked as the :term:`entry
|
|
block`. Every EBB ends with a :term:`terminator instruction`, so execution can
|
|
never fall through to the next EBB without an explicit branch.
|
|
|
|
A ``.cton`` file consists of a sequence of independent function definitions:
|
|
|
|
.. productionlist::
|
|
function-list : { function }
|
|
function : function-spec "{" preamble function-body "}"
|
|
function-spec : "function" function-name signature
|
|
preamble : { preamble-decl }
|
|
function-body : { extended-basic-block }
|
|
|
|
Static single assignment form
|
|
-----------------------------
|
|
|
|
The instructions in the function body use and produce *values* in SSA form. This
|
|
means that every value is defined exactly once, and every use of a value must be
|
|
dominated by the definition.
|
|
|
|
Cretonne does not have phi instructions but uses *EBB arguments* instead. An EBB
|
|
can be defined with a list of typed arguments. Whenever control is transferred
|
|
to the EBB, values for the arguments must be provided. When entering a function,
|
|
the incoming function arguments are passed as arguments to the entry EBB.
|
|
|
|
Instructions define zero, one, or more result values. All SSA values are either
|
|
EBB arguments or instruction results.
|
|
|
|
In the example above, the loop induction variable ``i`` is represented as three
|
|
SSA values: In the entry block, ``v4`` is the initial value. In the loop block
|
|
``ebb2``, the EBB argument ``v5`` represents the value of the induction
|
|
variable during each iteration. Finally, ``v12`` is computed as the induction
|
|
variable value for the next iteration.
|
|
|
|
It can be difficult to generate correct SSA form if the program being converted
|
|
into Cretonne IL contains multiple assignments to the same variables. Such
|
|
variables can be presented to Cretonne as :term:`stack slot`\s instead. Stack
|
|
slots are accessed with the :inst:`stack_store` and :inst:`stack_load`
|
|
instructions which behave more like variable accesses in a typical programming
|
|
language. Cretonne can perform the necessary dataflow analysis to convert stack
|
|
slots to SSA form.
|
|
|
|
|
|
Value types
|
|
===========
|
|
|
|
All SSA values have a type which determines the size and shape (for SIMD
|
|
vectors) of the value. Many instructions are polymorphic -- they can operate on
|
|
different types.
|
|
|
|
Boolean types
|
|
-------------
|
|
|
|
Boolean values are either true or false. While this only requires a single bit
|
|
to represent, more bits are often used when holding a boolean value in a
|
|
register or in memory. The :type:`b1` type represents an abstract boolean
|
|
value. It can only exist as an SSA value, it can't be stored in memory or
|
|
converted to another type. The larger boolean types can be stored in memory.
|
|
|
|
.. todo:: Clarify the representation of larger boolean types.
|
|
|
|
The multi-bit boolean types can be interpreted in different ways. We could
|
|
declare that zero means false and non-zero means true. This may require
|
|
unwanted normalization code in some places.
|
|
|
|
We could specify a fixed encoding like all ones for true. This would then
|
|
lead to undefined behavior if untrusted code uses the multibit booleans
|
|
incorrectly.
|
|
|
|
Something like this:
|
|
|
|
- External code is not allowed to load/store multi-bit booleans or
|
|
otherwise expose the representation.
|
|
- Each target specifies the exact representation of a multi-bit boolean.
|
|
|
|
.. autoctontype:: b1
|
|
.. autoctontype:: b8
|
|
.. autoctontype:: b16
|
|
.. autoctontype:: b32
|
|
.. autoctontype:: b64
|
|
|
|
Integer types
|
|
-------------
|
|
|
|
Integer values have a fixed size and can be interpreted as either signed or
|
|
unsigned. Some instructions will interpret an operand as a signed or unsigned
|
|
number, others don't care.
|
|
|
|
.. autoctontype:: i8
|
|
.. autoctontype:: i16
|
|
.. autoctontype:: i32
|
|
.. autoctontype:: i64
|
|
|
|
Floating point types
|
|
--------------------
|
|
|
|
The floating point types have the IEEE semantics that are supported by most
|
|
hardware. There is no support for higher-precision types like quads or
|
|
double-double formats.
|
|
|
|
.. autoctontype:: f32
|
|
.. autoctontype:: f64
|
|
|
|
SIMD vector types
|
|
-----------------
|
|
|
|
A SIMD vector type represents a vector of values from one of the scalar types
|
|
(boolean, integer, and floating point). Each scalar value in a SIMD type is
|
|
called a *lane*. The number of lanes must be a power of two in the range 2-256.
|
|
|
|
.. type:: i%Bx%N
|
|
|
|
A SIMD vector of integers. The lane type :type:`iB` is one of the integer
|
|
types :type:`i8` ... :type:`i64`.
|
|
|
|
Some concrete integer vector types are :type:`i32x4`, :type:`i64x8`, and
|
|
:type:`i16x4`.
|
|
|
|
The size of a SIMD integer vector in memory is :math:`N B\over 8` bytes.
|
|
|
|
.. type:: f32x%N
|
|
|
|
A SIMD vector of single precision floating point numbers.
|
|
|
|
Some concrete :type:`f32` vector types are: :type:`f32x2`, :type:`f32x4`,
|
|
and :type:`f32x8`.
|
|
|
|
The size of a :type:`f32` vector in memory is :math:`4N` bytes.
|
|
|
|
.. type:: f64x%N
|
|
|
|
A SIMD vector of double precision floating point numbers.
|
|
|
|
Some concrete :type:`f64` vector types are: :type:`f64x2`, :type:`f64x4`,
|
|
and :type:`f64x8`.
|
|
|
|
The size of a :type:`f64` vector in memory is :math:`8N` bytes.
|
|
|
|
.. type:: b1x%N
|
|
|
|
A boolean SIMD vector.
|
|
|
|
Boolean vectors are used when comparing SIMD vectors. For example,
|
|
comparing two :type:`i32x4` values would produce a :type:`b1x4` result.
|
|
|
|
Like the :type:`b1` type, a boolean vector cannot be stored in memory.
|
|
|
|
Pseudo-types and type classes
|
|
-----------------------------
|
|
|
|
These are not concrete types, but convenient names uses to refer to real types
|
|
in this reference.
|
|
|
|
.. type:: iPtr
|
|
|
|
A Pointer-sized integer.
|
|
|
|
This is either :type:`i32`, or :type:`i64`, depending on whether the target
|
|
platform has 32-bit or 64-bit pointers.
|
|
|
|
.. type:: iB
|
|
|
|
Any of the scalar integer types :type:`i8` -- :type:`i64`.
|
|
|
|
.. type:: Int
|
|
|
|
Any scalar *or vector* integer type: :type:`iB` or :type:`iBxN`.
|
|
|
|
.. type:: fB
|
|
|
|
Either of the floating point scalar types: :type:`f32` or :type:`f64`.
|
|
|
|
.. type:: Float
|
|
|
|
Any scalar *or vector* floating point type: :type:`fB` or :type:`fBxN`.
|
|
|
|
.. type:: %Tx%N
|
|
|
|
Any SIMD vector type.
|
|
|
|
.. type:: Mem
|
|
|
|
Any type that can be stored in memory: :type:`Int` or :type:`Float`.
|
|
|
|
.. type:: Logic
|
|
|
|
Either :type:`b1` or :type:`b1xN`.
|
|
|
|
.. type:: Testable
|
|
|
|
Either :type:`b1` or :type:`iN`.
|
|
|
|
Immediate operand types
|
|
-----------------------
|
|
|
|
These types are not part of the normal SSA type system. They are used to
|
|
indicate the different kinds of immediate operands on an instruction.
|
|
|
|
.. type:: imm64
|
|
|
|
A 64-bit immediate integer. The value of this operand is interpreted as a
|
|
signed two's complement integer. Instruction encodings may limit the valid
|
|
range.
|
|
|
|
In the textual format, :type:`imm64` immediates appear as decimal or
|
|
hexadecimal literals using the same syntax as C.
|
|
|
|
.. type:: ieee32
|
|
|
|
A 32-bit immediate floating point number in the IEEE 754-2008 binary32
|
|
interchange format. All bit patterns are allowed.
|
|
|
|
.. type:: ieee64
|
|
|
|
A 64-bit immediate floating point number in the IEEE 754-2008 binary64
|
|
interchange format. All bit patterns are allowed.
|
|
|
|
.. type:: immvector
|
|
|
|
An immediate SIMD vector. This operand supplies all the bits of a SIMD
|
|
type, so it can have different sizes depending on the type produced. The
|
|
bits of the operand are interpreted as if the SIMD vector was loaded from
|
|
memory containing the immediate.
|
|
|
|
.. type:: intcc
|
|
|
|
An integer condition code. See the :inst:`icmp` instruction for details.
|
|
|
|
.. type:: floatcc
|
|
|
|
A floating point condition code. See the :inst:`fcmp` instruction for details.
|
|
|
|
The two IEEE floating point immediate types :type:`ieee32` and :type:`ieee64`
|
|
are displayed as hexadecimal floating point literals in the textual IL format.
|
|
Decimal floating point literals are not allowed because some computer systems
|
|
can round differently when converting to binary. The hexadecimal floating point
|
|
format is mostly the same as the one used by C99, but extended to represent all
|
|
NaN bit patterns:
|
|
|
|
Normal numbers
|
|
Compatible with C99: ``-0x1.Tpe`` where ``T`` are the trailing
|
|
significand bits encoded as hexadecimal, and ``e`` is the unbiased exponent
|
|
as a decimal number. :type:`ieee32` has 23 trailing significand bits. They
|
|
are padded with an extra LSB to produce 6 hexadecimal digits. This is not
|
|
necessary for :type:`ieee64` which has 52 trailing significand bits
|
|
forming 13 hexadecimal digits with no padding.
|
|
|
|
Zeros
|
|
Positive and negative zero are displayed as ``0.0`` and ``-0.0`` respectively.
|
|
|
|
Subnormal numbers
|
|
Compatible with C99: ``-0x0.Tpemin`` where ``T`` are the trailing
|
|
significand bits encoded as hexadecimal, and ``emin`` is the minimum exponent
|
|
as a decimal number.
|
|
|
|
Infinities
|
|
Either ``-Inf`` or ``Inf``.
|
|
|
|
Quiet NaNs
|
|
Quiet NaNs have the MSB of the trailing significand set. If the remaining
|
|
bits of the trailing significand are all zero, the value is displayed as
|
|
``-NaN`` or ``NaN``. Otherwise, ``-NaN:0xT`` where ``T`` are the trailing
|
|
significand bits encoded as hexadecimal.
|
|
|
|
Signaling NaNs
|
|
Displayed as ``-sNaN:0xT``.
|
|
|
|
Control flow
|
|
============
|
|
|
|
Branches transfer control to a new EBB and provide values for the target EBB's
|
|
arguments, if it has any. Conditional branches only take the branch if their
|
|
condition is satisfied, otherwise execution continues at the following
|
|
instruction in the EBB.
|
|
|
|
.. autoinst:: jump
|
|
.. autoinst:: brz
|
|
.. autoinst:: brnz
|
|
.. autoinst:: br_table
|
|
|
|
.. inst:: JT = jump_table EBB0, EBB1, ..., EBBn
|
|
|
|
Declare a jump table in the :term:`function preamble`.
|
|
|
|
This declares a jump table for use by the :inst:`br_table` indirect branch
|
|
instruction. Entries in the table are either EBB names, or ``0`` which
|
|
indicates an absent entry.
|
|
|
|
The EBBs listed must belong to the current function, and they can't have
|
|
any arguments.
|
|
|
|
:arg EBB0: Target EBB when ``x = 0``.
|
|
:arg EBB1: Target EBB when ``x = 1``.
|
|
:arg EBBn: Target EBB when ``x = n``.
|
|
:result: A jump table identifier. (Not an SSA value).
|
|
|
|
Traps stop the program because something went wrong. The exact behavior depends
|
|
on the target instruction set architecture and operating system. There are
|
|
explicit trap instructions defined below, but some instructions may also cause
|
|
traps for certain input value. For example, :inst:`udiv` traps when the divisor
|
|
is zero.
|
|
|
|
.. autoinst:: trap
|
|
.. autoinst:: trapz
|
|
.. autoinst:: trapnz
|
|
|
|
|
|
Function calls
|
|
==============
|
|
|
|
A function call needs a target function and a :term:`function signature`. The
|
|
target function may be determined dynamically at runtime, but the signature
|
|
must be known when the function call is compiled. The function signature
|
|
describes how to call the function, including arguments, return values, and the
|
|
calling convention:
|
|
|
|
.. productionlist::
|
|
signature : "(" [arglist] ")" ["->" retlist] [call_conv]
|
|
arglist : arg { "," arg }
|
|
retlist : arglist
|
|
arg : type { flag }
|
|
flag : "uext" | "sext" | "inreg"
|
|
callconv : `string`
|
|
|
|
Arguments and return values have flags whose meaning is mostly target
|
|
dependent. They make it possible to call native functions on the target
|
|
platform. When calling other Cretonne functions, the flags are not necessary.
|
|
|
|
Functions that are called directly must be declared in the :term:`function
|
|
preamble`:
|
|
|
|
.. inst:: F = function NAME signature
|
|
|
|
Declare a function so it can be called directly.
|
|
|
|
:arg NAME: Name of the function, passed to the linker for resolution.
|
|
:arg signature: Function signature. See below.
|
|
:result F: A function identifier that can be used with :inst:`call`.
|
|
|
|
.. inst:: a, b, ... = call F(args...)
|
|
|
|
Direct function call.
|
|
|
|
:arg F: Function identifier to call, declared by :inst:`function`.
|
|
:arg args...: Function arguments matching the signature of F.
|
|
:result a,b,...: Return values matching the signature of F.
|
|
|
|
.. autoinst:: x_return
|
|
|
|
This simple example illustrates direct function calls and signatures::
|
|
|
|
function gcd(i32 uext, i32 uext) -> i32 uext "C" {
|
|
f1 = function divmod(i32 uext, i32 uext) -> i32 uext, i32 uext
|
|
|
|
entry ebb1(v1: i32, v2: i32):
|
|
brz v2, ebb2
|
|
v3, v4 = call f1(v1, v2)
|
|
br ebb1(v2, v4)
|
|
|
|
ebb2:
|
|
return v1
|
|
}
|
|
|
|
Indirect function calls use a signature declared in the preamble.
|
|
|
|
.. inst:: SIG = signature signature
|
|
|
|
Declare a function signature for use with indirect calls.
|
|
|
|
:arg signature: Function signature. See :token:`signature`.
|
|
:result SIG: A signature identifier.
|
|
|
|
.. inst:: a, b, ... = call_indirect SIG, x(args...)
|
|
|
|
Indirect function call.
|
|
|
|
:arg SIG: A function signature identifier declared with :inst:`signature`.
|
|
:arg iPtr x: The address of the function to call.
|
|
:arg args...: Function arguments matching SIG.
|
|
:result a,b,...: Return values matching SIG.
|
|
|
|
.. todo:: Define safe indirect function calls.
|
|
|
|
The :inst:`call_indirect` instruction is dangerous to use in a sandboxed
|
|
environment since it is not easy to verify the callee address.
|
|
We need a table-driven indirect call instruction, similar to
|
|
:inst:`br_table`.
|
|
|
|
|
|
Memory
|
|
======
|
|
|
|
Cretonne provides fully general :inst:`load` and :inst:`store` instructions for
|
|
accessing memory. However, it can be very complicated to verify the safety of
|
|
general loads and stores when compiling code for a sandboxed environment, so
|
|
Cretonne also provides more restricted memory operations that are always safe.
|
|
|
|
.. inst:: a = load p, Offset, Flags...
|
|
|
|
Load from memory at ``p + Offset``.
|
|
|
|
This is a polymorphic instruction that can load any value type which has a
|
|
memory representation.
|
|
|
|
:arg iPtr p: Base address.
|
|
:arg Offset: Immediate signed offset.
|
|
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
|
|
:flag aligntrap: Always trap if the memory access is misaligned.
|
|
:result T a: Loaded value.
|
|
|
|
.. inst:: store x, p, Offset, Flags...
|
|
|
|
Store ``x`` to memory at ``p + Offset``.
|
|
|
|
This is a polymorphic instruction that can store any value type with a
|
|
memory representation.
|
|
|
|
:arg T x: Value to store.
|
|
:arg iPtr p: Base address.
|
|
:arg Offset: Immediate signed offset.
|
|
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
|
|
:flag aligntrap: Always trap if the memory access is misaligned.
|
|
|
|
Loads and stores are *misaligned* if the resultant address is not a multiple of
|
|
the expected alignment. Depending on the target architecture, misaligned memory
|
|
accesses may trap, or they may work. Sometimes, operating systems catch
|
|
alignment traps and emulate the misaligned memory access.
|
|
|
|
On target architectures like x86 that don't check alignment, Cretonne expands
|
|
the aligntrap flag into a conditional trap instruction::
|
|
|
|
v5 = load.i32 v1, 4, align(4), aligntrap
|
|
; Becomes:
|
|
v10 = and_imm v1, 3
|
|
trapnz v10
|
|
v5 = load.i32 v1, 4
|
|
|
|
|
|
Local variables
|
|
---------------
|
|
|
|
One set of restricted memory operations access the current function's stack
|
|
frame. The stack frame is divided into fixed-size stack slots that are
|
|
allocated in the :term:`function preamble`. Stack slots are not typed, they
|
|
simply represent a contiguous sequence of bytes in the stack frame.
|
|
|
|
.. inst:: SS = stack_slot Bytes, Flags...
|
|
|
|
Allocate a stack slot in the preamble.
|
|
|
|
If no alignment is specified, Cretonne will pick an appropriate alignment
|
|
for the stack slot based on its size and access patterns.
|
|
|
|
:arg Bytes: Stack slot size on bytes.
|
|
:flag align(N): Request at least N bytes alignment.
|
|
:result SS: Stack slot index.
|
|
|
|
.. inst:: a = stack_load SS, Offset
|
|
|
|
Load a value from a stack slot at the constant offset.
|
|
|
|
This is a polymorphic instruction that can load any value type which has a
|
|
memory representation.
|
|
|
|
The offset is an immediate constant, not an SSA value. The memory access
|
|
cannot go out of bounds, i.e. ``sizeof(a) + Offset <= sizeof(SS)``.
|
|
|
|
:arg SS: Stack slot declared with :inst:`stack_slot`.
|
|
:arg Offset: Immediate non-negative offset.
|
|
:result T a: Value loaded.
|
|
|
|
.. inst:: stack_store x, SS, Offset
|
|
|
|
Store a value to a stack slot at a constant offset.
|
|
|
|
This is a polymorphic instruction that can store any value type with a
|
|
memory representation.
|
|
|
|
The offset is an immediate constant, not an SSA value. The memory access
|
|
cannot go out of bounds, i.e. ``sizeof(a) + Offset <= sizeof(SS)``.
|
|
|
|
:arg T x: Value to be stored.
|
|
:arg SS: Stack slot declared with :inst:`stack_slot`.
|
|
:arg Offset: Immediate non-negative offset.
|
|
|
|
The dedicated stack access instructions are easy ofr the compiler to reason
|
|
about because stack slots and offsets are fixed at compile time. For example,
|
|
the alignment of these stack memory accesses can be inferred from the offsets
|
|
and stack slot alignments.
|
|
|
|
It can be necessary to escape from the safety of the restricted instructions by
|
|
taking the address of a stack slot.
|
|
|
|
.. inst:: a = stack_addr SS, Offset
|
|
|
|
Get the address of a stack slot.
|
|
|
|
Compute the absolute address of a byte in a stack slot. The offset must
|
|
refer to a byte inside the stack slot: ``0 <= Offset < sizeof(SS)``.
|
|
|
|
:arg SS: Stack slot declared with :inst:`stack_slot`.
|
|
:arg Offset: Immediate non-negative offset.
|
|
:result iPtr a: Address.
|
|
|
|
The :inst:`stack_addr` instruction can be used to macro-expand the stack access
|
|
instructions before instruction selection::
|
|
|
|
v1 = stack_load.f64 ss3, 16
|
|
; Expands to:
|
|
v9 = stack_addr ss3, 16
|
|
v1 = load.f64 v9
|
|
|
|
Heaps
|
|
-----
|
|
|
|
Code compiled from WebAssembly or asm.js runs in a sandbox where it can't access
|
|
all process memory. Instead, it is given a small set of memory areas to work
|
|
in, and all accesses are bounds checked. Cretonne models this through the
|
|
concept of *heaps*.
|
|
|
|
A heap is declared in the function preamble and can be accessed with restricted
|
|
instructions that trap on out-of-bounds accesses. Heap addresses can be smaller
|
|
than the native pointer size, for example unsigned :type:`i32` offsets on a
|
|
64-bit architecture.
|
|
|
|
.. inst:: H = heap Name
|
|
|
|
Declare a heap in the function preamble.
|
|
|
|
This doesn't allocate memory, it just retrieves a handle to a sandbox from
|
|
the runtime environment.
|
|
|
|
:arg Name: String identifying the heap in the runtime environment.
|
|
:result H: Heap identifier.
|
|
|
|
.. inst:: a = heap_load H, p, Offset
|
|
|
|
Load a value at the address ``p + Offset`` in the heap H.
|
|
|
|
Trap if the heap access would be out of bounds.
|
|
|
|
:arg H: Heap identifier created by :inst:`heap`.
|
|
:arg iN p: Unsigned base address in heap.
|
|
:arg Offset: Immediate signed offset.
|
|
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
|
|
:flag aligntrap: Always trap if the memory access is misaligned.
|
|
:result T a: Loaded value.
|
|
|
|
.. inst:: a = heap_store H, x, p, Offset
|
|
|
|
Store a value at the address ``p + Offset`` in the heap H.
|
|
|
|
Trap if the heap access would be out of bounds.
|
|
|
|
:arg H: Heap indetifier created by :inst:`heap`.
|
|
:arg T x: Value to be stored.
|
|
:arg iN p: Unsigned base address in heap.
|
|
:arg Offset: Immediate signed offset.
|
|
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
|
|
:flag aligntrap: Always trap if the memory access is misaligned.
|
|
|
|
When optimizing heap accesses, Cretonne may separate the heap bounds checking
|
|
and address computations from the memory accesses.
|
|
|
|
.. inst:: a = heap_addr H, p, Size
|
|
|
|
Bounds check and compute absolute address of heap memory.
|
|
|
|
Verify that the address range ``p .. p + Size - 1`` is valid in the heap H,
|
|
and trap if not.
|
|
|
|
Convert the heap-relative address in ``p`` to a real absolute address and
|
|
return it.
|
|
|
|
:arg H: Heap identifier created by :inst:`heap`.
|
|
:arg iN p: Unsigned base address in heap.
|
|
:arg Size: Immediate unsigned byte count for range to verify.
|
|
:result iPtr a: Absolute address corresponding to ``p``.
|
|
|
|
A small example using heaps::
|
|
|
|
function vdup(i32, i32) {
|
|
h1 = heap "main"
|
|
|
|
entry ebb1(v1: i32, v2: i32):
|
|
v3 = heap_load.i32x4 h1, v1, 0
|
|
v4 = heap_addr h1, v2, 32 ; Shared range check for two stores.
|
|
store v3, v4, 0
|
|
store v3, v4, 16
|
|
return
|
|
}
|
|
|
|
The final expansion of the :inst:`heap_addr` range check and address conversion
|
|
depends on the runtime environment.
|
|
|
|
|
|
Operations
|
|
==========
|
|
|
|
The remaining instruction set is mostly arithmetic.
|
|
|
|
A few instructions have variants that take immediate operands (e.g.,
|
|
:inst:`band` / :inst:`band_imm`), but in general an instruction is required to
|
|
load a constant into an SSA value.
|
|
|
|
.. autoinst:: iconst
|
|
.. autoinst:: f32const
|
|
.. autoinst:: f64const
|
|
.. autoinst:: vconst
|
|
.. autoinst:: select
|
|
|
|
Vector operations
|
|
-----------------
|
|
|
|
.. autoinst:: vselect
|
|
|
|
.. inst:: a = vbuild x, y, z, ...
|
|
|
|
Vector build.
|
|
|
|
Build a vector value from the provided lanes.
|
|
|
|
.. autoinst:: splat
|
|
.. autoinst:: insertlane
|
|
.. autoinst:: extractlane
|
|
|
|
Integer operations
|
|
------------------
|
|
|
|
.. autoinst:: icmp
|
|
.. autoinst:: iadd
|
|
.. autoinst:: iadd_imm
|
|
.. autoinst:: isub
|
|
.. autoinst:: isub_imm
|
|
|
|
.. todo:: Integer overflow arithmetic
|
|
|
|
Add instructions for add with carry out / carry in and so on. Enough to
|
|
implement larger integer types efficiently. It should also be possible to
|
|
legalize :type:`i64` arithmetic to terms of :type:`i32` operations.
|
|
|
|
.. autoinst:: imul
|
|
.. autoinst:: imul_imm
|
|
|
|
.. todo:: Larger multiplication results.
|
|
|
|
For example, ``smulx`` which multiplies :type:`i32` operands to produce a
|
|
:type:`i64` result. Alternatively, ``smulhi`` and ``smullo`` pairs.
|
|
|
|
.. autoinst:: udiv
|
|
.. autoinst:: udiv_imm
|
|
.. autoinst:: sdiv
|
|
.. autoinst:: sdiv_imm
|
|
.. autoinst:: urem
|
|
.. autoinst:: urem_imm
|
|
.. autoinst:: srem
|
|
.. autoinst:: srem_imm
|
|
|
|
.. todo:: Minimum / maximum.
|
|
|
|
NEON has ``smin``, ``smax``, ``umin``, and ``umax`` instructions. We should
|
|
replicate those for both scalar and vector integer types. Even if the
|
|
target ISA doesn't have scalar operations, these are good pattern matching
|
|
targets.
|
|
|
|
.. todo:: Saturating arithmetic.
|
|
|
|
Mostly for SIMD use, but again these are good patterns for contraction.
|
|
Something like ``usatadd``, ``usatsub``, ``ssatadd``, and ``ssatsub`` is a
|
|
good start.
|
|
|
|
Bitwise operations
|
|
------------------
|
|
|
|
The bitwise operations and operate on any value type: Integers, floating point
|
|
numbers, and booleans. When operating on integer or floating point types, the
|
|
bitwise operations are working on the binary representation of the values. When
|
|
operating on boolean values, the bitwise operations work as logical operators.
|
|
|
|
.. autoinst:: band
|
|
.. autoinst:: bor
|
|
.. autoinst:: bxor
|
|
.. autoinst:: bnot
|
|
|
|
.. todo:: Redundant bitwise operators.
|
|
|
|
ARM has instructions like ``bic(x,y) = x & ~y``, ``orn(x,y) = x | ~y``, and
|
|
``eon(x,y) = x ^ ~y``.
|
|
|
|
The shift and rotate operations only work on integer types (scalar and vector).
|
|
The shift amount does not have to be the same type as the value being shifted.
|
|
Only the low `B` bits of the shift amount is significant.
|
|
|
|
When operating on an integer vector type, the shift amount is still a scalar
|
|
type, and all the lanes are shifted the same amount. The shift amount is masked
|
|
to the number of bits in a *lane*, not the full size of the vector type.
|
|
|
|
.. autoinst:: rotl
|
|
.. autoinst:: rotr
|
|
.. autoinst:: ishl
|
|
.. autoinst:: ushr
|
|
.. autoinst:: sshr
|
|
|
|
The bit-counting instructions below are scalar only.
|
|
|
|
.. autoinst:: clz
|
|
.. autoinst:: cls
|
|
.. autoinst:: ctz
|
|
.. autoinst:: popcnt
|
|
|
|
Floating point operations
|
|
-------------------------
|
|
|
|
These operations generally follow IEEE 754-2008 semantics.
|
|
|
|
.. autoinst:: fcmp
|
|
.. autoinst:: fadd
|
|
.. autoinst:: fsub
|
|
.. autoinst:: fmul
|
|
.. autoinst:: fdiv
|
|
.. autoinst:: sqrt
|
|
.. autoinst:: fma
|
|
|
|
Sign bit manipulations
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The sign manipulating instructions work as bitwise operations, so they don't
|
|
have special behavior for signaling NaN operands. The exponent and trailing
|
|
significand bits are always preserved.
|
|
|
|
.. autoinst:: fneg
|
|
.. autoinst:: fabs
|
|
.. autoinst:: fcopysign
|
|
|
|
Minimum and maximum
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
These instructions return the larger or smaller of their operands. They differ
|
|
in their handling of quiet NaN inputs. Note that signaling NaN operands always
|
|
cause a NaN result.
|
|
|
|
When comparing zeroes, these instructions behave as if :math:`-0.0 < 0.0`.
|
|
|
|
.. autoinst:: fmin
|
|
.. autoinst:: fminnum
|
|
.. autoinst:: fmax
|
|
.. autoinst:: fmaxnum
|
|
|
|
Rounding
|
|
~~~~~~~~
|
|
|
|
These instructions round their argument to a nearby integral value, still
|
|
represented as a floating point number.
|
|
|
|
.. autoinst:: ceil
|
|
.. autoinst:: floor
|
|
.. autoinst:: trunc
|
|
.. autoinst:: nearest
|
|
|
|
Conversion operations
|
|
---------------------
|
|
|
|
.. autoinst:: bitcast
|
|
.. autoinst:: ireduce
|
|
.. autoinst:: uextend
|
|
.. autoinst:: sextend
|
|
.. autoinst:: fpromote
|
|
.. autoinst:: fdemote
|
|
.. autoinst:: fcvt_to_uint
|
|
.. autoinst:: fcvt_to_sint
|
|
.. autoinst:: fcvt_from_uint
|
|
.. autoinst:: fcvt_from_sint
|
|
|
|
Glossary
|
|
========
|
|
|
|
.. glossary::
|
|
|
|
function signature
|
|
A function signature describes how to call a function. It consists of:
|
|
|
|
- The calling convention.
|
|
- The number of arguments and return values. (Functions can return
|
|
multiple values.)
|
|
- Type and flags of each argument.
|
|
- Type and flags of each return value.
|
|
|
|
Not all function atributes are part of the signature. For example, a
|
|
function that never returns could be marked as ``noreturn``, but that
|
|
is not necessary to know when calling it, so it is just an attribute,
|
|
and not part of the signature.
|
|
|
|
function preamble
|
|
A list of declarations of entities that are used by the function body.
|
|
Some of the entities that can be declared in the preamble are:
|
|
|
|
- Local variables.
|
|
- Functions that are called directly.
|
|
- Function signatures for indirect function calls.
|
|
- Function flags and attributes that are not part of the signature.
|
|
|
|
function body
|
|
The extended basic blocks which contain all the executable code in a
|
|
function. The function body follows the function preamble.
|
|
|
|
basic block
|
|
A maximal sequence of instructions that can only be entered from the
|
|
top, and that contains no branch or terminator instructions except for
|
|
the last instruction.
|
|
|
|
extended basic block
|
|
EBB
|
|
A maximal sequence of instructions that can only be entered from the
|
|
top, and that contains no :term:`terminator instruction`\s except for
|
|
the last one. An EBB can contain conditional branches that can fall
|
|
through to the following instructions in the block, but only the first
|
|
instruction in the EBB can be a branch target.
|
|
|
|
The last instrution in an EBB must be a :term:`terminator instruction`,
|
|
so execion cannot flow through to the next EBB in the function. (But
|
|
there may be a branch to the next EBB.)
|
|
|
|
Note that some textbooks define an EBB as a maximal *subtree* in the
|
|
control flow graph where only the root can be a join node. This
|
|
definition is not equivalent to Cretonne EBBs.
|
|
|
|
terminator instruction
|
|
A control flow instruction that unconditionally directs the flow of
|
|
execution somewhere else. Execution never continues at the instruction
|
|
following a terminator instruction.
|
|
|
|
The basic terminator instructions are :inst:`br`, :inst:`return`, and
|
|
:inst:`trap`. Conditional branches and instructions that trap
|
|
conditionally are not terminator instructions.
|
|
|
|
entry block
|
|
The :term:`EBB` that is executed first in a function. Currently, a
|
|
Cretonne function must have exactly one entry block. The types of the
|
|
entry block arguments must match the types of arguments in the function
|
|
signature.
|
|
|
|
stack slot
|
|
A fixed size memory allocation in the current function's activation
|
|
frame. Also called a local variable.
|
|
|