Commit 868f8fd4 authored by Kunshan Wang's avatar Kunshan Wang

Use frame cursors in stack introspection.

Introduced the framecursorref type. This is to solve performance problem
when the stack is very deep. The API is still stateless, but a frame
cursor is mutable. A frame cursor can only be created from a stack in
the READY<Ts> state, may iterate downwards, and must be explicitly
closed. Concurrent introspection that races with stack modification (pop
frames/push frames/resuming a stack) has undefined behaviour.

Also used "resumption point" to simplify the description of stack
binding. The semantics is not changed. In the concrete syntax,
resumption points are "beginning of function", "OSR point instruction"
or "the appropriate place for native function". The Mu cases can be
mapped to the resumption points in the formal model: entry of the %entry
block, and the exit of the block where the OSR point instruction is in.
parent d99118c3
......@@ -367,57 +367,73 @@ Stack introspection
::
[0x254]@uvm.meta.cur_func (%stack: stackref, %frame: int<32>) -> int<32>
[0x255]@uvm.meta.cur_func_Ver (%stack: stackref, %frame: int<32>) -> int<32>
[0x256]@uvm.meta.cur_inst (%stack: stackref, %frame: int<32>) -> int<32>
[0x257]@uvm.meta.dump_keepalives (%stack: stackref, %frame: int<32>) -> @uvm.meta.refs.r
[0x254]@uvm.meta.new_cursor (%stack: stackref) -> framecursorref
[0x255]@uvm.meta.next_frame (%cursor: framecursorref)
[0x256]@uvm.meta.copy_cursor (%cursor: framecursorref) -> framecursorref
[0x257]@uvm.meta.close_cursor (%cursor: framecursorref)
``%stack`` and ``%frame`` selects a frame in the stack ``%stack``. 0 is the top,
1 is the next top, ... ``%stack`` must not be null.
In all cases, ``cursor`` and ``stack`` cannot be ``NULL``.
- ``cur_func`` returns the ID of the current function of ``%frame`` in
``%stack``. Returns 0 if the frame is native.
- ``new_cursor`` allocates a frame cursor, referring to the top frame of
``%stack``. Returns the frame cursor reference.
- ``cur_func_ver`` returns the ID of the current function version of ``%frame``
in ``%stack``. Returns 0 if the frame is native, or the function of the frame
is undefined.
- ``next_frame`` moves the frame cursor so that it refers to the frame below its
current frame.
- ``cur_inst`` returns the ID of the current instruction of ``%frame`` in
``%stack``. Returns 0 if the frame is just created, its function is undefined,
or the frame is native.
- ``copy_cursor`` allocates a frame cursor which refers to the same frame as
``%cursor``. Returns the frame cursor reference.
- ``close_cursor`` deallocates the cursor.
::
[0x258]@uvm.meta.cur_func (%cursor: framecursorref) -> int<32>
[0x259]@uvm.meta.cur_func_Ver (%cursor: framecursorref) -> int<32>
[0x25a]@uvm.meta.cur_inst (%cursor: framecursorref) -> int<32>
[0x25b]@uvm.meta.dump_keepalives (%cursor: framecursorref) -> @uvm.meta.refs.r
These functions operate on the frame referred by ``%cursor``. In all cases,
``%cursor`` cannot be ``NULL``.
- ``cur_func`` returns the ID of the frame. Returns 0 if the frame is native.
- ``cur_func_ver`` returns the ID of the current function version of the frame.
Returns 0 if the frame is native, or the function of the frame is undefined.
- ``cur_inst`` returns the ID of the current instruction of the frame. Returns 0
if the frame is just created, its function is undefined, or the frame is
native.
- ``dump_keepalives`` dumps the values of the keep-alive variables of the
current instruction in the selected frame. If the function is undefined, the
arguments are the keep-alive variables. Cannot be used on native frames. The
return value is a list of object references, each of which refers to an object
which has type *T* and contains value *v*, where *T* and *v* are the type and
the value of the corresponding keep-alive variable, respectively.
current instruction in the frame. If the function is undefined, the arguments
are the keep-alive variables. Cannot be used on native frames. The return
value is a list of object references, each of which refers to an object which
has type *T* and contains value *v*, where *T* and *v* are the type and the
value of the corresponding keep-alive variable, respectively.
On-stack replacement
--------------------
::
[0x258]@uvm.meta.pop_frame (%stack: stackref)
[0x259]@uvm.meta.push_frame <[sig]> (%stack: stackref, %func: funcref<sig>)
[0x25c]@uvm.meta.pop_frames_to (%cursor: framecursorref)
[0x25d]@uvm.meta.push_frame <[sig]> (%stack: stackref, %func: funcref<sig>)
``%stack`` and ``%func`` must not be ``NULL``.
``%cursor``, ``%stack`` and ``%func`` must not be ``NULL``.
- ``pop_frame`` pops the top frame of the stack ``%stack``.
- ``pop_frames_to`` pops all frames above ``%cursor``.
- ``push_frame`` creates a new frame on top of the stack ``%stack`` for the Mu
function ``%func``. ``%func`` must have the signature ``sig``.
The previous top frame must be in the **READY<Ts>** state for some Ts. The
return type of ``%func`` must be *Ts*.
- ``push_frame`` creates a new frame on top of the stack ``%stack`` for the
current version of the Mu function ``%func``. ``%func`` must have the
signature ``sig``.
Watchpoint operations
---------------------
::
[0x25a]@uvm.meta.enable_watchpoint (%wpid: int<32>)
[0x25b]@uvm.meta.disable_watchpoint (%wpid: int<32>)
[0x25e]@uvm.meta.enable_watchpoint (%wpid: int<32>)
[0x25f]@uvm.meta.disable_watchpoint (%wpid: int<32>)
- ``enable_watchpoint`` enables all watchpoints of watchpoint ID ``%wpid``.
- ``disenable_watchpoint`` disables all watchpoints of watchpoint ID ``%wpid``.
......@@ -427,7 +443,7 @@ Trap handling
::
[0x25c]@uvm.meta.set_trap_handler (%handler: funcref<@uvm.meta.trap_handler.sig>, %userdata: ref<void>)
[0x260]@uvm.meta.set_trap_handler (%handler: funcref<@uvm.meta.trap_handler.sig>, %userdata: ref<void>)
This instruction registers a trap handler. ``%handler`` is the function to be
called and ``%userdata`` will be their last argument when called.
......
......@@ -361,8 +361,8 @@ The ``cur_func_ver`` function, in addition to returning the function version
ID, it may also return 0 if the selected frame is a native frame. (Multiple
native frames are counted as one between two Mu frames.)
The ``pop_frame`` function has implementation-defined behaviours when popping
native frames.
The ``pop_frames_to`` function has implementation-defined behaviours when
popping native frames.
When rebinding a thread to a stack with a value, and the top frame is on a call
site (native or Mu), the value associated with the rebinding is the return value
......
This diff is collapsed.
......@@ -46,6 +46,7 @@ The following type constructors are available in Mu:
- funcref < sig >
- threadref
- stackref
- framecursorref
- tagref64
- vector < T length >
- uptr < T >
......@@ -60,7 +61,8 @@ There are several kinds of types.
* ``float`` and ``double`` are **floating point types**.
* ``ref`` and ``weakref`` are **object referenct types**.
* ``ref``, ``iref`` and ``weakref`` are **reference types**.
* ``funcref``, ``threadref`` and ``stackref`` are **opaque reference types**.
* ``funcref``, ``threadref``, ``stackref`` and ``framecursorref`` are **opaque
reference types**.
* *Reference types* and *opaque reference types* are **general reference types**.
* ``int``, ``float``, ``double``, *general reference types* and ``tagref64`` are
**scalar types**.
......@@ -71,8 +73,8 @@ There are several kinds of types.
* ``hybrid`` is the only **variable-length type**. All other types are
**fixed-length types**.
* ``uptr`` and ``ufuncptr`` are **pointer types**.
* ``int``, ``ref``, ``iref``, ``funcref``, ``stackref``, ``threadref`` and
pointer types are **EQ-comparable types**.
* ``int``, ``ref``, ``iref``, ``funcref``, ``stackref``, ``threadref``,
``framecursorref`` and pointer types are **EQ-comparable types**.
* ``int``, ``iref`` and pointer types are **ULT-comparable types**.
* ``ref<T>`` is the **strong variant** of ``weakref<T>``; ``weakref<T>`` is the
**weak variant** of ``ref<T>``. All other types are the strong variant or weak
......@@ -101,8 +103,9 @@ following:
the ``ufuncptr`` type is not well-formed.
* All other types are not native-safe. (Specifically, they are ``ref``,
``iref``, ``weakref``, ``funcref``, ``threadref``, ``stackref`` as well as
``struct``, ``array`` or ``hybrid`` that contains them.)
``iref``, ``weakref``, ``funcref``, ``threadref``, ``stackref``,
``framecursorref`` as well as ``struct``, ``array`` or ``hybrid`` that
contains them.)
Integer Type
------------
......@@ -539,13 +542,25 @@ A ``NULL`` value of a ``funcref`` type does not refer to any function.
| 0x0D |
+------+
``threadref`` and ``stackref`` are opaque reference types to Mu threads and Mu
stacks, respectively. They are not interchangeable with reference types. Only
some special instructions (e.g. ``@uvm.new_stack``, ``NEWTHREAD``) or API
calls can operate on them.
``framecursorref``
Both ``threadref`` and ``stackref`` values can be ``NULL``, which does not refer
to any threads or stacks.
+------+
| opct |
+======+
| 0x12 |
+------+
``threadref``, ``stackref`` and ``framecursorref`` are opaque reference types to
Mu threads, Mu stacks and frame cursors, respectively. They are not
interchangeable with reference types. Only some special instructions (e.g.
``@uvm.new_stack``, ``NEWTHREAD``, ``@uvm.meta.new_cursor``) or API calls can
operate on them.
All opaque reference values can be ``NULL``, which does not refer to anything.
A frame cursor is an internal structure which is used by the stack introspection
API to iterate through stack frames. Its content is mutable but opaque. See
`Threads and Stacks <threads-stacks.rest>`__ for more details.
Tagged Reference
----------------
......
......@@ -113,6 +113,7 @@ opaque **handles**. Those handles are defined as::
typedef void *MuFuncRefValue; // funcref<sig>
typedef void *MuThreadRefValue; // threadref
typedef void *MuStackRefValue; // stackref
typedef void *MuFCRefValue; // framecursorref
typedef void *MuTagRef64Value; // tagref64
typedef void *MuUPtrValue; // uptr
typedef void *MuUFPValue; // ufuncptr
......@@ -709,26 +710,48 @@ Stack introspection
::
MuID (*cur_func )(MuCtx *ctx, MuStackRefValue stack, int frame);
MuID (*cur_func_ver )(MuCtx *ctx, MuStackRefValue stack, int frame);
MuID (*cur_inst )(MuCtx *ctx, MuStackRefValue stack, int frame);
void (*dump_keepalives)(MuCtx *ctx, MuStackRefValue stack, int frame, MuValue *results);
MuFCRefValue (*new_cursor )(MuCtx *ctx, MuStackRefValue stack);
void (*next_frame )(MuCtx *ctx, MuFCRefValue cursor);
MuFCRefValue (*copy_cursor )(MuCtx *ctx, MuFCRefValue cursor);
void (*close_cursor)(MuCtx *ctx, MuFCRefValue cursor);
* ``cur_func`` returns the ID of the current function of ``frame``
in ``stack``. Returns 0 if the frame is native.
In all cases, ``cursor`` and ``stack`` cannot be ``NULL``.
* ``cur_func_ver`` returns the ID of the current function version of ``frame``
in ``stack``. Returns 0 if the frame is native, or the function of the frame
is undefined.
* ``new_cursor`` allocates a frame cursor, referring to the top frame of
``stack``. Returns the frame cursor reference.
* ``cur_inst`` returns the ID of the current instruction of ``frame`` in
``stack``. Returns 0 if the frame is just created, its function is undefined,
or the frame is native.
* ``next_frame`` moves the frame cursor so that it refers to the frame below its
current frame.
* ``copy_cursor`` allocates a frame cursor which refers to the same frame as
``cursor``. Returns the frame cursor reference.
* ``close_cursor`` deallocates the cursor.
::
MuID (*cur_func )(MuCtx *ctx, MuFCRefValue cursor);
MuID (*cur_func_ver )(MuCtx *ctx, MuFCRefValue cursor);
MuID (*cur_inst )(MuCtx *ctx, MuFCRefValue cursor);
void (*dump_keepalives)(MuCtx *ctx, MuFCRefValue cursor, MuValue *results);
These functions operate on the frame referred by ``cursor``. In all cases,
``cursor`` cannot be the Mu value ``NULL``, and ``results`` cannot be the C
pointer ``NULL``.
* ``cur_func`` returns the ID of the frame. Returns 0 if the frame is native.
* ``cur_func_ver`` returns the ID of the current function version of the frame.
Returns 0 if the frame is native, or the function of the frame is undefined.
* ``cur_inst`` returns the ID of the current instruction of the frame. Returns 0
if the frame is just created, its function is undefined, or the frame is
native.
* ``dump_keepalives`` dumps the values of the keep-alive variables of the
current instruction in the selected frame. If the function is undefined, the
arguments are the keep-alive variables. Cannot be used on native frames. As
many handles as the keep-alive variables are written in the ``results`` array.
current instruction of the frame. If the function is undefined, the arguments
are the keep-alive variables. Cannot be used on native frames. As many
handles as the keep-alive variables are written in the ``results`` array.
..
......@@ -736,10 +759,6 @@ Stack introspection
variables, so the client can always know how long the ``results`` array
should be allocated.
The frame is the ``frame``-th frame below the top frame, starting with 0. (If
``frame`` is 0, it is the top frame. 1 is the frame below the top. 2 is the one
below 1, and so on.)
..
For Lua users: The debug interface provides many similar introspection
......@@ -757,26 +776,21 @@ On-stack replacement
::
void (*pop_frame )(MuCtx *ctx, MuStackRefValue stack);
void (*push_frame)(MuCtx *ctx, MuStackRefValue stack, MuFuncRefValue func);
* ``pop_frame`` pops the top frame of the stack. Popping native frames has
implementation-defined behaviour.
void (*pop_frames_to)(MuCtx *ctx, MuFCRefValue cursor);
void (*push_frame )(MuCtx *ctx, MuStackRefValue stack, MuFuncRefValue func);
* ``push_frame`` creates a new frame on the top of ``stack`` for function
``func``.
``cursor``, ``stack`` and ``func`` cannot be ``NULL``.
The previous top frame must be in the **READY<Ts>** state for some Ts. The
return type of ``%func`` must be *Ts*.
* ``pop_frames_to`` pops all frames above ``cursor``.
After ``push_frame``, the new top frame is in the **READY<Ts>** state where Ts
are the argument types of ``func``. When the stack is rebound, it continues
from the beginning of ``func``. The return value or exceptions from ``func``
are received by the previous frame.
* ``push_frame`` creates a new frame on the top of ``stack`` for the current
version of function ``func``.
..
For JVM users: ``pop_frame`` is similar to the ``PopFrame`` JVM TI function.
For JVM users: ``pop_frames_to`` is similar to the ``PopFrame`` JVM TI
function, but the resumption point is immediately after the call site, not
immediately before.
Tagged reference operations
......
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