Commit 9fd4cac2 authored by Kunshan Wang's avatar Kunshan Wang

Make the void type more like unit.

void has a NULL value. Removed the RETVOID instruciton and the SWAPSTACK
PASS_VOID clause, and the corresponding API that involves PASS_VOID.
parent f66a9265
......@@ -48,6 +48,8 @@ present::
.const @NULLREF <@refvoid> = NULL
.const @VOID <@void> = NULL
SSA Variables
=============
......@@ -1146,7 +1148,7 @@ If the previous basic block executed before this basic block is one of the
BRANCH %head
%exit:
RET <@i64> %aa
RET %aa
}
When branching from ``%body`` to ``%head``, the value of both ``%aa`` and
......@@ -1332,48 +1334,38 @@ function. After returning from the client, this instruction will be tried again.
// handle the exception
}
``RET`` and ``RETVOID`` Instruction
-----------------------------------
``RET`` ``<`` *T* ``>`` *rv*
``RET`` Instruction
-------------------
``RETVOID``
``RET`` *rv*
T
*type*: The type of *rv*.
rv
*variable* of type *T*: The return value of the current function.
*variable* of type *T*, where *T* is the return type of the current
function: The return value of the current function.
RET:
+------+-----+-----+
| opct | idt | idt |
+======+=====+=====+
| 0x63 | T | rv |
+------+-----+-----+
RETVOID:
+------+
| opct |
+======+
| 0x64 |
+------+
+------+-----+
| opct | idt |
+======+=====+
| 0x63 | rv |
+------+-----+
The ``RET`` instruction returns from the current function with *rv* as the
return value of the current function. The ``RETVOID`` instruction returns from
the current function whose return type is void.
return value of the current function. The type of *rv* must be the return type
of the current function.
In order to return from a function with return type ``void``, the ``RET``
instruction should return ``NULL``, the only value of the ``void`` type.
The return type of the ``RET`` and the ``RETVOID`` instruction themselves are
``void``.
The return type of the ``RET`` instruction itself is always ``void``.
..
NOTE: Mu allows giving name to a ``RET`` or ``RETVOID`` instruction like
``%ret_inst = RET <@double> @SOME_CONSTANT``, but is is usually not useful.
In this case the type of ``%ret_inst`` is always ``void``. The
``@SOME_CONSTANT`` value is returned to the caller, not the instruction's
SSA variable.
NOTE: Mu allows giving name to a ``RET`` instruction like ``%ret_inst = RET
@SOME_CONSTANT``, but is is usually not useful. In this case the type of
``%ret_inst`` is always ``void``. The ``@SOME_CONSTANT`` value is returned
to the **caller**, not the instruction's SSA variable.
..
......@@ -1389,12 +1381,12 @@ The return type of the ``RET`` and the ``RETVOID`` instruction themselves are
.funcdef @sum VERSION @sum_v1 <@sig1> (%x %y) {
%entry:
%s = ADD <@double> %x %y
RET <@double> %s
RET %s
}
.funcdef @main VERSION @main_v1 <@sig2> () {
%entry:
RETVOID
RET @VOID
}
``THROW`` Instruction
......@@ -2605,7 +2597,11 @@ The return value of ``CCALL`` is the return value of the native callee.
``CCALL`` cannot receive exceptions thrown by C++.
``CCALL`` is an OSR point.
``CCALL`` is an OSR point. A stack may enter the ``READY<T>`` state with some Mu
frames stopping on ``CCALL`` instructions. If the top frame is stopping at this
instruction, the value passed to the stack when rebinding becomes the return
value of this instruction. However, whether a native frame can be popped is
implementation-defined.
For LLVM users: Mu is not designed to be compatible with C and functions
defined in Mu IR does not use the native C ABI. This instruction is
......@@ -2750,7 +2746,6 @@ function. After returning from the client, this instruction will be tried again.
- *curStackClause* ::= ``KILL_OLD``
+ *newStackClause* ::= ``PASS_VALUE`` ``<`` *T2* ``>`` *val*
+ *newStackClause* ::= ``PASS_VOID``
+ *newStackClause* ::= ``THROW_EXC`` *exc*
``SWAPSTACK``:
......@@ -2785,20 +2780,12 @@ function. After returning from the client, this instruction will be tried again.
| 0x01 | T2 | val |
+------+-----+-----+
*newStackClause* with ``PASS_VOID``:
+------+
| opct |
+======+
| 0x02 |
+------+
*newStackClause* with ``THROW_EXC``:
+------+-----+
| opct | idt |
+======+=====+
| 0x03 | exc |
| 0x02 | exc |
+------+-----+
swappee
......@@ -2825,27 +2812,31 @@ keepAliveClause
The ``SWAPSTACK`` instruction unbinds the current thread with the current stack
and rebinds it to another stack *swappee*.
If the *curStackClause* is ``RET_WITH``, then the swapper will enter the
``READY<T1>`` state. If the *curStackClause* is ``KILL_OLD``, then the swapper
will enter the ``DEAD`` state.
In the *curStackClause*:
If the *newStackClause* is ``PASS_VALUE``, then it passes the value *val* of
type *T2* is passed to the *swappee* stack. If the *newStackClause* is
``PASS_VOID``, then it *passes void* (see `<threads-stacks>`__) to the *swappee*
stack. If the *newStackClause* is ``THROW_EXC``, the exception object *exc* is
thrown to the *swappee* stack.
- If the *curStackClause* is ``RET_WITH``, then the swapper will enter the
``READY<T1>`` state.
- If the *curStackClause* is ``KILL_OLD``, then the swapper will enter the
``DEAD`` state.
In the *newStackClause*:
+ If the *newStackClause* is ``PASS_VALUE``, then it passes the value *val* of
type *T2* to the *swappee* stack. *T2* can be ``void``, in which
case *val* has to have the ``NULL`` value.
+ If the *newStackClause* is ``THROW_EXC``, the exception object *exc* is thrown
to the *swappee* stack.
See `Thread and Stack <threads-stacks>`__ for more details about the SWAP-STACK
operation.
When a stack in the ``READY<T>`` state is pausing on a ``SWAPSTACK`` instruction
and is being re-bound to a thread, then it continues normally with the return
value being the value received, or continues exceptionally, catching the
exception received.
..
TODO: Move the previous paragraph to the `<threads-stacks>`__ chapter and
unify the description of ``TRAP``, ``WATCHPOINT`` and ``SWAPSTACK`` since
all are supposed to use SWAP-STACK as the underlying mechanism.
The ``SWAPSTACK`` instruction is an OSR point.
..
......@@ -2891,7 +2882,7 @@ The ``SWAPSTACK`` instruction is an OSR point.
%head:
// Go to the coroutine
%cur_num = SWAPSTACK %coro RET_WITH <@i64> PASS_VOID EXC(%body %exit)
%cur_num = SWAPSTACK %coro RET_WITH <@i64> PASS_VALUE <@void> @VOID EXC(%body %exit)
%body:
// Do something with %cur_num
......
......@@ -18,6 +18,7 @@ typedef void *MuIRefValue; // iref<T>
typedef void *MuStructValue; // struct<...>
typedef void *MuArrayValue; // array<T l>
typedef void *MuVectorValue; // vector<T l>
typedef void *MuVoidValue; // void
typedef void *MuFuncRefValue; // funcref<sig>
typedef void *MuThreadRefValue; // threadref
typedef void *MuStackRefValue; // stackref
......@@ -38,8 +39,7 @@ typedef int MuTrapHandlerResult;
#define MU_THREAD_EXIT 0x00
#define MU_REBIND_PASS_VALUE 0x01
#define MU_REBIND_PASS_VOID 0x02
#define MU_REBIND_THROW_EXC 0x03
#define MU_REBIND_THROW_EXC 0x02
// Declare the types here because they are used in the following signatures.
typedef struct MuVM MuVM;
......
......@@ -107,12 +107,15 @@ is called the **stack-bottom frame** and the function is called the
implementation can still have its own frames or useful data below the
stack-bottom frame. They are implementation-specific details.
A newly created stack is in the **READY<void>** state.
A newly created stack is in the **READY<void>** state. When bound (explained
later) and passed the ``void`` value (``NULL``), it continues from the first
instruction of its stack-bottom function.
When a thread is created, a stack must be provided as its **initial stack**. The
initial stack must be in the **READY<void>** state and will enter the **ACTIVE**
state after the thread is created. A newly created thread can execute
immediately.
initial stack must be in the **READY<void>** state. The new thread binds
(explained later) to the initial stack, passing the ``void`` value (``NULL``) to
it, thus the stack will enter the **ACTIVE** state after the thread is created.
A newly created thread can execute immediately.
NOTE: Unlike Java, there is not a separate step to "start" a thread. A
thread starts when it is created.
......@@ -140,47 +143,23 @@ instruction, the ``new_thread`` API function and the trap handler, can bind a
thread to a stack.
When **binding** a thread to a stack, the state of the stack changes from
**READY<T>** to **ACTIVE**. In this process, one of the following three actions
**READY<T>** to **ACTIVE**. In this process, one of the following two actions
shall be performed on the stack:
- A binding operation can **pass a value** of type *T* to the stack. In this
case, the stack must be in the **READY<T>** state where *T* is not ``void``,
and it **receives the value**.
- A binding operation can **pass void** to the stack. In this case, the stack
must be in the **READY<void>** state and it **receives void**.
NOTE: "Pass void" is just a more explicit way to say "not pass a value, but
continue normally".
case, the stack must be in the **READY<T>** state, and it **receives the
value**. *T* can be ``void``.
- A binding operation can **raise an exception** to the stack. In this case, the
stack can be in **READY<T>** with any *T* (including ``void``) and it
**receives the exception**.
stack can be in **READY<T>** with any *T* and it **receives the exception**.
It gives undefined behaviour if the stack is not in the expected state.
Newly created stacks are in the **READY<void>** state. A newly created thread
binds to the provided initial stack and *passes void* to it. When a newly
created stack is bound to a thread and *receives void*, it continues execution
from the beginning of the stack-bottom function.
The state of a stack is **READY<void>** after the ``push_frame`` API function.
When such a stack is rebound receiving void, it continues from the beginning of
the function. When such a stack is rebound receiving exception, the exception is
thrown out of the top frame.
When binding to a frame and the top frame is a native frame, it must be calling
back to Mu. The value passed from Mu becomes the value returned from Mu. If the
native frame expects void return value, passing void will return to the native
code. The expected type is previous Mu callee which has been popped via OSR
(this is the only case when a native frame can be the top frame of an unbound
stack).
Unbinding
---------
Some actions, including the ``@uvm.thread_exit``, ``TRAP``, ``WATCHPOINT`` the
``SWAPSTACK`` instruction, can unbind a thread from a stack.
Some actions, including the ``@uvm.thread_exit``, ``TRAP``, ``WATCHPOINT`` and
the ``SWAPSTACK`` instruction, can unbind a thread from a stack.
When **unbinding** a thread from a stack, one of the following two actions shall
be performed on the stack:
......@@ -230,12 +209,13 @@ function pops the top frame from a stack. The ``push_frame`` function creates a
new frame for a given function and its arguments on the top of a stack.
After popping a frame from a stack, the next stack below the top becomes the new
top frame and its current instruction must be a ``CALL`` or a ``CCALL``
instruction. The stack now enters the ``READY<T>`` state where *T* is the return
type of the ``CALL`` or ``CCALL`` instruction (may be ``void``). The value
passed to such a stack will be received by the ``CALL`` or ``CCALL`` instruction
as if its callee returned the value. The exception passed to such a stack can be
caught by the ``CALL`` instruction as if it is thrown by the callee.
top frame. At this moment, either the new frame is a native frame or it is a Mu
frame pausing on an OSR point instruction. In either case, the stack now enters
the ``READY<T>`` state where *T* (may be ``void``) is the return type of the
frame just popped. The value passed to such a stack will be received by the
native caller, or the OSR-point instruction. If an exception is thrown, it is
thrown into the native frame, which has implementation-defined behaviours, or to
the Mu frame, where the OSR-point instruction can catch the exception.
After pushing a frame to a stack, it enters the ``READY<void>`` state and will
continue from the beginning of the function of the frame. Throwing an exception
......
......@@ -320,8 +320,9 @@ Struct
| 0x07 | nfields | T1 | T2 | ... |
+------+---------+-----+-----+-----+
``struct`` is a Cartesian product type of several types. *T1*, *T2*, *...* are
its **field types**. A ``struct`` must have at least one member.
A ``struct`` is a Cartesian product type of several types. *T1*, *T2*, *...* are
its **field types**. A ``struct`` must have at least one member. ``struct``
members cannot be ``void``.
NOTE: For C programs: C does not allow empty structures, either, but many
programmers create empty structures in practice. C++ does allow empty
......@@ -338,7 +339,9 @@ cannot be the type of an SSA variable.
In the binary form, an integer literal ``nfields`` determines the number of
fields. Exactly that number of type IDs follows the ``nfields`` literal.
For LLVM users: this is identical to LLVM's struct type.
For LLVM users: This is the same as LLVM's structure type, except structures
with a "flexible array element" (a 0-length array as the last element)
corresponds to the ``hybrid`` type in Mu.
..
......@@ -369,11 +372,11 @@ Array
| 0x08 | T | length |
+------+-----+--------+
An ``array`` is a sequence of homogeneous data structure in the memory. *T* is
its **element type**, i.e. the type of its elements, and *length* is the length
of the array.
An ``array`` is a sequence of values of the same type. *T* is its **element
type**, i.e. the type of its elements, and *length* is the length of the array.
*T* must not be ``void``. An array must have at least one element.
It is not recommended to have an SSA variable of ``array`` type.
It is not recommended to have SSA variables of ``array`` type.
NOTE: The most useful feature of arrays is indexing by a variable index.
But an SSA variable has more in common with registers than memory, and SSA
......@@ -389,9 +392,9 @@ It is not recommended to have an SSA variable of ``array`` type.
..
For LLVM users: **An array always has a fixed length**. There is no type for
"array of run-time-determined length" in the Mu type system. The closest
counterpart is the ``hybrid`` type.
For LLVM users: Like LLVM arrays, a Mu array must have a size, but cannot
have size 0. The closest counterpart of the "variable length array" (VLA)
type in C is the ``hybrid`` type.
..
......@@ -428,12 +431,19 @@ decided at allocation time. *F* is the type of the fixed part. *V* is the type
of the *elements* of the variable part.
NOTE: This is intended to play the part of "struct with flexible array
member" in C99.
member" in C99, i.e. ``struct { F fixedPart; V varPart[]; }``.
*F* can be ``void``. In this case, the fixed part is empty, and the variable
part is in the beginning of this ``hybrid`` memory location, like a
variable-length array without a header. *V* cannot be ``void``.
``hybrid`` cannot be the type of any SSA variable.
NOTE: There can be references to hybrids.
``hybrid`` is the only type in Mu whose length is determined at allocation site
rather than determined by the type itself.
..
Example::
......@@ -460,13 +470,44 @@ Void Type
| 0x0A |
+------+
The ``void`` type has no value. It can only be used in the following ways:
The ``void`` type has exactly one value: ``NULL``.
NOTE: Having exactly one value is equivalent to having no value: both convey
no information at all. It is usually implemented as not being represented by
any bytes.
It can only be used in the following ways:
* As the type of allocation units that do not store any values.
This allows allocating ``void`` in the heap/stack/global memory. Particularly,
the ``NEW`` instruction with the type ``void`` creates a new empty heap object
which is not the same as any others. This is similar to the ``new Object()``
expression in Java.
``ref<void>``, ``iref<void>``, ``weakref<void>`` and ``uptr<void>`` are also
allowed.
* As the type of allocation units that do not represent values. Hence it is
usable as the referent type of reference types and pointer types.
* As the fixed part of a ``hybrid`` to indicate the absence of the fixed part.
* As the type of instructions or the return type of functions that do not return
values.
* As the return type of instructions and functions, and the type of passed value
during SWAP-STACK, to indicate that the instruction/function/swap-stack
operation does not return/pass any meaningful value.
For this reason, the following instructions may take a ``void`` value, i.e.
``NULL``, as their arguments:
* ``RET``: when returning from a function that returns ``void``.
* ``SWAPSTACK`` in the ``PASS_VAL`` clause: when the swappee is expecting a
``void`` value to be passed.
In addition to many instructions that always return ``void``, the following
instructions may return ``void`` if the callee/swappee returns/passes
``void``:
* ``CALL``
* ``CCALL``
* ``SWAPSTACK``
Opaque Reference Type
---------------------
......@@ -588,6 +629,7 @@ Vector Type
``vector`` is the vector type for single-instruction multiple-data (SIMD)
operations. A ``vector`` value is a packed value of multiple values of the same
type. *T* is the type of its elements and *length* is the number of elements.
*T* cannot be void. *length* must be at least one.
It is allowed to have SSA variables of vector types.
......
......@@ -911,8 +911,7 @@ where::
#define MU_THREAD_EXIT 0x00
#define MU_REBIND_PASS_VALUE 0x01
#define MU_REBIND_PASS_VOID 0x02
#define MU_REBIND_THROW_EXC 0x03
#define MU_REBIND_THROW_EXC 0x02
``ctx`` is a new client context created for this particular trap event.
``thread`` is a threadref to the thread that causes the trap. ``stack`` is
......@@ -930,11 +929,16 @@ Before returning, the trap handler should set ``*result``:
* ``Mu_REBIND_PASS_VALUE``: The thread ``thread`` will be rebound to a stack
``*new_stack``. The value ``*value`` is passed to ``*new_stack``. The type of
``*value`` must match the type expected by ``*new_stack``.
* ``Mu_REBIND_PASS_VOID``: The thread ``thread`` will be rebound to a stack
``*new_stack``. It passes ``void`` to it.
* ``Mu_REBIND_THROW_EXC``: The thread ``thread`` will be rebound to a stack
``*new_stack``. It throws exception ``*exception`` to the stack.
..
NOTE: More often than not, it is desirable to pass the ``void`` value:
``NULL``. The client should have pre-defined a Mu ``void`` constant, such
as: ``.const @VOID <@void> = NULL``. Then this constant can be obtained by
``ctx->handle_from_const``.
In all cases, if ``*new_stack``, ``*value`` and/or ``*exception`` are used, they
must be set and must be held by ``ctx``.
......@@ -942,7 +946,7 @@ The signature of undefined function handlers is::
typedef void (*MuUndefFuncHandler)(MuCtx *ctx, MuID func_id, MuCPtr userdata);
``funct_id`` is the ID of the callee function. ``userdata`` is the pointer
``func_id`` is the ID of the callee function. ``userdata`` is the pointer
provided when registering the handler.
After returning, the Mu program will re-execute the same instruction again.
......
......@@ -666,12 +666,14 @@ which is one of the following:
- ``LOAD``, ``STORE``, ``CMPXCHG``, ``ATOMICRMW``
- ``TRAP``, ``WATCHPOINT``
- ``NEWSTACK``, ``SWAPSTACK``
- Some common instructions via the generic ``COMMINST`` instruction (currently
none)
- Some `common instructions <common-insts>`__ via the generic ``COMMINST``
instruction:
- ``@uvm.thread_exit``
- ``BRANCH``, ``BRANCH2``, ``SWITCH``
- ``TAILCALL``
- ``RET``, ``RETVOID``
- ``RET``
- ``THROW``
..
......@@ -743,7 +745,7 @@ the function. Defines of functions (bundle loading) and uses of functions
(including function calls and the creation of stacks, i.e. the ``NEWSTACK``
instruction or the ``new_stack`` API) obey the memory model of the ``RELAXED``
order as if the definition is a store and the use is a load. See `Memory Model
<memory-model>``__.
<memory-model>`__.
All existing activations of any functions remain unchanged, that is, they remain
to be the old versions of the functions.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment