struct VmOp
Defined at line 102 of file ../../src/developer/debug/zxdb/expr/vm_op.h
Holds a bytecode operation type (VmOpType) and any parameters associated with it.
Most bytecode machines would encode any parameters like strings constants or a binary operator
type compactly, either in the byte string, or using a small representation of a reference to
some other stream.
Our programs are so small compared to the type of system we expect to run on we do not care
about space. Instead, this implementation prefers a safe, simple approach. As part of this,
each VM operation is a bytecode operation combined with a relatively large variant holding any
ancillary data required by the operation. For many operations, this is very wasteful, but means
we can avoid doing error-prone reading of variable data from the instruction stream and the
decode logic becomes trivial.
See vm_exec.cc for execution details.
Local variables
---------------
In most "real" stack-based bycode machines, the local variables would be stored on the stack
and referenced by index from the "stack pointer" (the position of the stack and the entrypoint
of the current function). It is simple and efficient.
But it requires very careful tracking of where variables can be declared such that the block
that contains them can pop its local variables when the block exits: a local variable declaration
must never be conditionally executed since then the containing block wouldn't know whether to
clean it up. Any mistakes will corrupt the VM stack and are difficult to debug.
Our parser supports multiple languages and plays a bit fast-and-loose with where declarations can
appear. This could be fixed but since we also care much more about debugability and simplicity
of the parser than of performance, we have dedicated storage for local variables.
As local variables are parsed, we assign each one an index based on the parser depth just like
the stack-based approach. But these refer into a separate array specific to local variables.
This adds a copy for each variable declaration and some extra memory allocations for the separate
array but neither of these matter to us. The local variables created inside a scope are cleared
by the kPopLocals command which will be emitted at the end of a block. The block knows the size
to shrink to because it knows how many local variables are in scope at the entry to the block.
But this approach doesn't care if a local variable declaration was skipped (that slot will just
be unused).
Example:
{ // The parser remembers the # of locals in scope at the opening of the block.
// This info is saved on the block parse node for the last step.
int i = 23; // The parser adds info about "i" and saves its index as the local variable
// slot. This slot is used in the op kSetLocal.
i = 19; // The parser looks up the variable index and uses it in a kGetLocal op.
} // At the exit of a block, the parser emits kPopLocals to set the # locals back
// the size it was at the opening of the block.
Variable assignment
-------------------
Our expression language doesn't have lvalues. The assignment operator (binary operator "=")
always fully evaluates the thing on the left-hand-side. This simplifies things by providing only
one code path for all evaluating (rather than having a code path to compute where the thing would
be without computing its value).
All ExprValues have an ExprValueSource which tracks where they came from originally. This
information is used to update the variable when modified. For program values this is normally a
memory address or a register. For debugger-local variables, we keep a refptr to the source of
the value. The LocalExprValue object is a refcounted container for local variables that can
have such references to them.
Break
-----
The "break" keyword (e.g. for a loop) is weird and difficult. You need to execute the cleanup
code for every construct between the break and the loop, but not any of the remaining code in
them.
Our break is implemented somewhat like an exception. At the top of a loop, the loop emits the
"PushBreak" opcode (like the "try" instruction of an exception handler). This sets the
destination stream address to jump to and saves the stack and local variable stack. The "break"
opcode jumps to the nearest "set break" destination (like a thrown exception), and pops the stack
and local variable stack size back to the values they were at the top of the loop (like exception
unwinding code).
The bottom of the loop executes a "PopBreak" command which releases the registration of that
loop.
Public Members
VmOpType op
ExprToken token
variant info
static const uint32_t kBadJumpDest
Public Methods
void SetJumpDest (uint32_t dest)
Sets the destination of the this operator's jump destination to the given value. This will
assert if this operation is not a jump.
This is used commonly because the destination of a jump is often unknown until additional code
is filled in.
Defined at line 14 of file ../../src/developer/debug/zxdb/expr/vm_op.cc
VmOp MakeError (Err err, ExprToken token)
Constructor helper functions.
Defined at line 124 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeUnary (ExprToken token)
Defined at line 127 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeBinary (ExprToken token)
Defined at line 130 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeExpandRef (ExprToken token)
Defined at line 133 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeDrop (ExprToken token)
Defined at line 136 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeDup (ExprToken token)
Defined at line 139 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeLiteral (ExprValue value, ExprToken token)
Defined at line 142 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeJump (uint32_t dest, ExprToken token)
Defined at line 147 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeJumpIfFalse (uint32_t dest, ExprToken token)
Defined at line 150 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeGetLocal (uint32_t slot, ExprToken token)
Defined at line 154 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeSetLocal (uint32_t slot, ExprToken token)
Defined at line 158 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakePopLocals (uint32_t entry_count, ExprToken token)
Defined at line 162 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakePushBreak (uint32_t dest, ExprToken token)
Defined at line 167 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakePopBreak (ExprToken token)
Defined at line 171 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeBreak (ExprToken token)
Defined at line 174 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeCallback0 (Callback0 cb, ExprToken token)
Defined at line 177 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeCallback1 (Callback1 cb, ExprToken token)
Defined at line 180 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeCallback2 (Callback2 cb, ExprToken token)
Defined at line 183 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeCallbackN (int num_params, CallbackN cb, ExprToken token)
Defined at line 186 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeAsyncCallback0 (AsyncCallback0 cb, ExprToken token)
Defined at line 191 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeAsyncCallback1 (AsyncCallback1 cb, ExprToken token)
Defined at line 194 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeAsyncCallback2 (AsyncCallback2 cb, ExprToken token)
Defined at line 197 of file ../../src/developer/debug/zxdb/expr/vm_op.h
VmOp MakeAsyncCallbackN (int num_params, AsyncCallbackN cb, ExprToken token)
Defined at line 200 of file ../../src/developer/debug/zxdb/expr/vm_op.h