Update language reference.
Add a glossary and explain the overall shape of a Cretonne function.
This commit is contained in:
8
docs/example.c
Normal file
8
docs/example.c
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
float
|
||||||
|
average(const float *array, size_t count)
|
||||||
|
{
|
||||||
|
double sum = 0;
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
sum += array[i];
|
||||||
|
return sum / count;
|
||||||
|
}
|
||||||
31
docs/example.cton
Normal file
31
docs/example.cton
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
function average(i32, i32) -> f32 {
|
||||||
|
ss1 = local 8, align 4 ; Stack slot for ``sum``.
|
||||||
|
|
||||||
|
entry ebb1(v1: i32, v2: i32):
|
||||||
|
v3 = fconst.f64 0.0
|
||||||
|
stack_store v3, ss1
|
||||||
|
brz v2, ebb3 ; Handle count == 0.
|
||||||
|
v4 = iconst.i32 0
|
||||||
|
br ebb2(v4)
|
||||||
|
|
||||||
|
ebb2(v5: i32):
|
||||||
|
v6 = imul_imm v5, 4
|
||||||
|
v7 = iadd v1, v6
|
||||||
|
v8 = heap_load.f32 v7 ; array[i]
|
||||||
|
v9 = fext.f64 v8
|
||||||
|
v10 = stack_load.f64 ss1
|
||||||
|
v11 = fadd v9, v10
|
||||||
|
stack_store v11, ss1
|
||||||
|
v12 = iadd_imm v5, 1
|
||||||
|
v13 = icmp ult v12, v2
|
||||||
|
brnz v13, ebb2(v12) ; Loop backedge.
|
||||||
|
v14 = stack_load.f64 ss1
|
||||||
|
v15 = cvt_utof.f64 v2
|
||||||
|
v16 = fdiv v14, v15
|
||||||
|
v17 = ftrunc.f32 v16
|
||||||
|
return v17
|
||||||
|
|
||||||
|
ebb3:
|
||||||
|
v100 = fconst.f32 0x7fc00000 ; 0/0 = NaN
|
||||||
|
return v100
|
||||||
|
}
|
||||||
144
docs/langref.rst
144
docs/langref.rst
@@ -21,62 +21,53 @@ 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
|
handles at the same time, but the functions don't share any data or reference
|
||||||
each other directly.
|
each other directly.
|
||||||
|
|
||||||
This is a C function that computes the average of an array of floats:
|
This is a simple C function that computes the average of an array of floats:
|
||||||
|
|
||||||
.. code-block:: c
|
.. literalinclude:: example.c
|
||||||
|
:language: c
|
||||||
|
|
||||||
float average(const float *array, size_t count) {
|
Here is the same function compiled into Cretonne IL:
|
||||||
double sum = 0;
|
|
||||||
for (size_t i = 0; i < count; i++)
|
|
||||||
sum += array[i];
|
|
||||||
return sum / count;
|
|
||||||
}
|
|
||||||
|
|
||||||
Here it is compiled into Cretonne IL::
|
.. literalinclude:: example.cton
|
||||||
|
:language: cton
|
||||||
|
:linenos:
|
||||||
|
:emphasize-lines: 2
|
||||||
|
|
||||||
function average(i32, i32) -> f32 {
|
The first line of a function definition provides the function *name* and
|
||||||
; Preamble.
|
the :term:`function signature` which declares the argument and return types.
|
||||||
ss1 = local 8, align 4
|
Then follows the :term:`function preample` which declares a number of entities
|
||||||
|
that can be referenced inside the function. In the example above, the preample
|
||||||
|
declares a single local variable, ``ss1``.
|
||||||
|
|
||||||
entry ebb1(v1: i32, v2: i32):
|
After the preample follows the :term:`function body` which consists of
|
||||||
v3 = fconst.f64 0.0
|
:term:`extended basic block`\s, one of which is marked as the :term:`entry
|
||||||
stack_store v3, ss1
|
block`. Every EBB ends with a :term:`terminator instruction`, and execution
|
||||||
brz v2, ebb3 ; Handle count == 0.
|
can never fall through to the next EBB without an explicit branch.
|
||||||
v4 = iconst.i32 0
|
|
||||||
br ebb2(v4)
|
|
||||||
|
|
||||||
ebb2(v5: i32):
|
Static single assignment form
|
||||||
; Compute address of array element.
|
-----------------------------
|
||||||
v6 = imul_imm v5, 4
|
|
||||||
v7 = iadd v1, v6
|
|
||||||
v8 = heap_load.f32 v7 ; array[i]
|
|
||||||
v9 = fext.f64 v8
|
|
||||||
; Add to accumulator in ss1.
|
|
||||||
v10 = stack_load.f64 ss1
|
|
||||||
v11 = fadd v9, v10
|
|
||||||
stack_store v11, ss1
|
|
||||||
; Increment loop counter.
|
|
||||||
v12 = iadd_imm v5, 1
|
|
||||||
v13 = icmp ult v12, v2
|
|
||||||
brnz v13, ebb2(v12) ; Loop backedge.
|
|
||||||
; Compute average from sum.
|
|
||||||
v14 = stack_load.f64 ss1
|
|
||||||
v15 = cvt_utof.f64 v2
|
|
||||||
v16 = fdiv v14, v15
|
|
||||||
v17 = ftrunc.f32 v16
|
|
||||||
return v17
|
|
||||||
|
|
||||||
ebb3:
|
The instructions in the function body use and produce *values* in SSA form. This
|
||||||
v100 = fconst.f32 0x7f800000 ; Inf
|
means that every value is defined exactly once, and every use of a value must be
|
||||||
return v100
|
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.
|
||||||
|
|
||||||
Type system
|
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.
|
||||||
|
|
||||||
|
Value types
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
|
||||||
All SSA values have a type which determines the size and shape (for SIMD
|
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
|
vectors) of the value. Many instructions are polymorphic -- they can operate on
|
||||||
different types.
|
different types.
|
||||||
@@ -653,3 +644,66 @@ Conversion operations
|
|||||||
.. inst:: a = cvt_utof x
|
.. inst:: a = cvt_utof x
|
||||||
.. inst:: a = cvt_stof x
|
.. inst:: a = cvt_stof x
|
||||||
|
|
||||||
|
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 preample
|
||||||
|
A list of declarations of entities that are used by the function body.
|
||||||
|
Some of the entities that can be declared in the preample 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|||||||
Reference in New Issue
Block a user