Skip to content

Commit c8c79a0

Browse files
committed
[[ BytecodeBlocks ]] Add bytecode blocks
This patch adds the ability to write bytecode directly inside 'bytecode' blocks in LCB: bytecode ... end bytecode Labels are local to the block and can be specified by using: myLabel: Temporaries are local to the block and can be specified by using: variable myTemp Temporaries, local variables and parameters can be used as register type arguments to opcodes. Module variables, defined constants and handler identifiers can be used as the 'source' parameter for the 'fetch' opcode. Module variables can be used as the 'dest' parameter for the 'source' opcode. Handler identifiers can be used as the 'handler' parameter for the 'invoke' opcode. The bytecode information is recorded in a simple array in script-builder.cpp - including name, opcode index and opcode parameter format. The script builder APIs have been thinned down to remove specific bytecode emission functions. Instead, bytecode is emitted using EmitBytecodeInModule. The emit module of lc-compile has been updated to use this.
1 parent 9a81c09 commit c8c79a0

20 files changed

Lines changed: 1595 additions & 341 deletions

File tree

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
group: reference
3+
---
4+
5+
# LiveCode Builder Bytecode Reference
6+
7+
## Introduction
8+
9+
The LiveCode Builder Virtual Machine is a infinite register machine with a
10+
high-level and verifiable bytecode.
11+
12+
All bytecode runs in the context of a module instance with executing handlers
13+
forming a stack of activation frames. Each frame contains an array of registers,
14+
the first part of which are parameters followed by handler-local variables and
15+
bytecode block registers.
16+
17+
Most bytecode operations operate directly on registers, access to module level
18+
definitions (handlers, constants, variables) are indirected through the fetch
19+
and store operations.
20+
21+
Each bytecode operation has an address which can be jumped to using the jump
22+
operations.
23+
24+
## Operations
25+
26+
### Jump
27+
28+
jump <label>
29+
30+
The jump operation sets the address of the next instruction to execute to
31+
that identified by <label>.
32+
33+
### Jump If False
34+
35+
jump_if_false <register>, <label>
36+
37+
The jump_if_true operation checks the value in <register> and jumps to <label>
38+
if it is 'false'.
39+
40+
If is a runtime error if <register> does not contain a boolean.
41+
42+
### Jump If True
43+
44+
jump_if_true <register>, <label>
45+
46+
The jump_if_true operation checks the value in <register> and jumps to <label>
47+
if it is 'true'.
48+
49+
If is a runtime error if <register> does not contain a boolean.
50+
51+
### Assign Constant
52+
53+
assign_constant <register>, <constant>
54+
55+
The assign_constant operation copies <constant> into <register>. Here <constant>
56+
can be the nothing literal, the true or false literal, an integer literal, a
57+
real literal, a string literal, a list literal or array literal.
58+
59+
If <register> is typed then it is a runtime error if the type of <constant>
60+
does not conform to that type.
61+
62+
### Assign
63+
64+
assign <dst-register>, <src-register>
65+
66+
The assign operation copies the value in <src-register> to <dst-register>.
67+
68+
If <dst-register> is typed then it is runtime error if the type of the value
69+
in <src-register> does not conform to that type.
70+
71+
### Return
72+
73+
return [ <result-reg> ]
74+
75+
The return operation exits the current handler, returning to the instruction after
76+
the invoke operation which created it. If present, the value in <result-reg> is
77+
copied as the return value. If not present then the return value will be 'nothing'.
78+
Additionally any values in out or inout parameters are copied back to the caller.
79+
80+
If <result-reg> is not present, then the return value will be 'nothing'.
81+
82+
It is a runtime error if the type of the return value does not conform to the
83+
return type of the current handler's signature.
84+
85+
It is a runtime error if any out or inout parameters are unassigned at the point
86+
of return.
87+
88+
### Invoke
89+
90+
invoke <handler>, <result>, <arg_1>, ..., <arg_n>
91+
92+
The invoke operation creates a new frame copying values from the argument registers
93+
for any in or inout parameters. It then starts executing the bytecode attached
94+
to <handler>.
95+
96+
If is a runtime error if the number of arguments provided is different from the
97+
signature of <handler>
98+
99+
If it a runtime error if for in and inout parameters, the contents of <arg_i>
100+
does not conform to the type of the parameter required by the signature.
101+
102+
### Invoke Indirect
103+
104+
invoke <handler-reg>, <result>, <arg_1>, ..., <arg_n>
105+
106+
The invoke indirect operation functions identically to the invoke operation
107+
except that it calls the handler in <handler-reg>.
108+
109+
It is a runtime error if <handler-reg> does not contain a handler value.
110+
111+
### Fetch
112+
113+
fetch <dst-register>, <definition>
114+
115+
The fetch operation copies the value of <definition> into <dst-register>. The
116+
<definition> may be a variable, constant or handler. In the case of a handler,
117+
a handler value is created.
118+
119+
It is a runtime error if the type of the value of <definition> does not conform
120+
to the type of <dst-register>.
121+
122+
### Store
123+
124+
store <src-register>, <definition>
125+
126+
The store operation copies the contents of <src-register> into <definition>. The
127+
<definition> must be a module level variable.
128+
129+
It is a runtime error if the type of the value in <src-register> does not conform
130+
to the type of <definition>.
131+
132+
### Assign List
133+
134+
assign_list <dst-reg>, <element1-reg>, ..., <elementn-reg>
135+
136+
The assign_array operation builds a list value from <element1-reg> up to
137+
<elementn-reg>.
138+
139+
It is a runtime error if the type of <dst-reg> does not conform to list.
140+
141+
### Assign Array
142+
143+
assign_array <dst-reg>, <key1-reg>, <value1-reg>, ..., <keyn-reg>, <valuen-reg>
144+
145+
The assign_array operation builds an array value from each key value pair
146+
<key1-reg>, <value1-reg> up to <keyn-reg>, <valuen-reg>.
147+
148+
It is a runtime error if the type of <dst-reg> does not conform to array.
149+
150+
### Reset
151+
152+
reset <reg>
153+
154+
The reset operation performs default initialization of <reg>. If the type of
155+
<reg> has no default value, it reverts to unassigned.

docs/guides/LiveCode Builder Language Reference.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ environment.
528528
| SetStatement
529529
| GetStatement
530530
| CallStatement
531+
| BytecodeStatement
531532

532533
There are a number of built-in statements which define control flow,
533534
variables, and basic variable transfer. The remaining syntax for
@@ -728,6 +729,40 @@ result** expression.
728729
> **Note:** All handlers return a value, even if it is nothing. This
729730
> means that calling a handler will always change **the result**.
730731
732+
### Bytecode Statements
733+
734+
BytecodeStatement
735+
: 'bytecode' SEPARATOR
736+
{ BytecodeOperation }
737+
'end'
738+
739+
BytecodeOperation
740+
: <Label: Identifier> ':'
741+
| 'register' <Name: Identifier> [ 'as' <Type: Type> ]
742+
| <Opcode: Identifier> { BytecodeArgument , ',' }
743+
744+
BytecodeArgument
745+
: ConstantValueExpression
746+
| Identifier
747+
748+
The bytecode statement allows bytecode to be written directly for the LiveCode
749+
Builder Virtual Machine.
750+
751+
Bytecode operation arguments can either be a constant expression, the name of a
752+
definition in current scope, the name of a register, or the name of a label in
753+
the current bytecode block. The exact opcodes and allowed arguments are defined
754+
in the LiveCode Builder Bytecode Reference.
755+
756+
Labels are local to the current bytecode block, and can be used as the target
757+
of one of the jump instructions.
758+
759+
Register definitions define a named register which is local to the current
760+
bytecode block. Registers are the same as handler-local variables except
761+
that they do not undergo default initialization.
762+
763+
> **Note:** Bytecode blocks are not intended for general use and the actual
764+
> available operations are subject to change.
765+
731766
## Expressions
732767

733768
Expression

docs/lcb/notes/17821.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# LiveCode Builder Language
2+
## Bytecode Blocks
3+
4+
* bytecode can now be directly written in handlers using a bytecode block:
5+
6+
bytecode
7+
register tTemp
8+
assign_constant tTemp, 1
9+
end bytecode
10+
11+
* for more details on what bytecode operations can be used see the LiveCode
12+
Builder Bytecode Reference
13+
14+
* bytecode blocks are not for general use and the current set of bytecode
15+
operations are subject to change

libscript/include/libscript/script.h

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,32 @@ bool MCScriptCallHandlerOfInstanceIfFound(MCScriptInstanceRef instance, MCNameRe
247247

248248
////////////////////////////////////////////////////////////////////////////////
249249

250+
enum MCScriptBytecodeParameterType
251+
{
252+
// Invalid index was passed to describe
253+
kMCScriptBytecodeParameterTypeUnknown,
254+
// The parameter should be a label index
255+
kMCScriptBytecodeParameterTypeLabel,
256+
// The parameter should be a register
257+
kMCScriptBytecodeParameterTypeRegister,
258+
// The parameter should be a constant pool index
259+
kMCScriptBytecodeParameterTypeConstant,
260+
// The parameter should be a fetchable definition (variable, constant, handler)
261+
kMCScriptBytecodeParameterTypeDefinition,
262+
// The parameter should be a variable definition
263+
kMCScriptBytecodeParameterTypeVariable,
264+
// The parameter should be a handler definition
265+
kMCScriptBytecodeParameterTypeHandler,
266+
};
267+
268+
bool MCScriptCopyBytecodeNames(MCProperListRef& r_proper_list);
269+
bool MCScriptLookupBytecode(const char *opname, uindex_t& r_opcode);
270+
const char *MCScriptDescribeBytecode(uindex_t opcode);
271+
MCScriptBytecodeParameterType MCScriptDescribeBytecodeParameter(uindex_t opcode, uindex_t index);
272+
bool MCScriptCheckBytecodeParameterCount(uindex_t opcode, uindex_t proposed_count);
273+
274+
////////////////////////////////////////////////////////////////////////////////
275+
250276
typedef struct MCScriptModuleBuilder *MCScriptModuleBuilderRef;
251277

252278
enum MCScriptModuleKind
@@ -313,7 +339,7 @@ void MCScriptBeginRecordTypeInModule(MCScriptModuleBuilderRef builder, uindex_t
313339
void MCScriptContinueRecordTypeInModule(MCScriptModuleBuilderRef builder, MCNameRef name, uindex_t type);
314340
void MCScriptEndRecordTypeInModule(MCScriptModuleBuilderRef builder, uindex_t& r_new_type);
315341

316-
void MCScriptAddDefinitionToModule(MCScriptModuleBuilderRef builder, uindex_t& r_index);
342+
void MCScriptAddDefinitionToModule(MCScriptModuleBuilderRef builder, MCScriptDefinitionKind kind, uindex_t& r_index);
317343

318344
void MCScriptAddTypeToModule(MCScriptModuleBuilderRef builder, MCNameRef name, uindex_t type, uindex_t index);
319345
void MCScriptAddConstantToModule(MCScriptModuleBuilderRef builder, MCNameRef name, uindex_t const_idx, uindex_t index);
@@ -345,30 +371,12 @@ void MCScriptAddEventToModule(MCScriptModuleBuilderRef builder, MCNameRef name,
345371

346372
void MCScriptDeferLabelForBytecodeInModule(MCScriptModuleBuilderRef builder, uindex_t& r_label);
347373
void MCScriptResolveLabelForBytecodeInModule(MCScriptModuleBuilderRef builder, uindex_t label);
348-
void MCScriptEmitJumpInModule(MCScriptModuleBuilderRef builder, uindex_t target_label);
349-
void MCScriptEmitJumpIfUndefinedInModule(MCScriptModuleBuilderRef builder, uindex_t value_reg, uindex_t target_label);
350-
void MCScriptEmitJumpIfDefinedInModule(MCScriptModuleBuilderRef builder, uindex_t value_reg, uindex_t target_label);
351-
void MCScriptEmitJumpIfFalseInModule(MCScriptModuleBuilderRef builder, uindex_t value_reg, uindex_t target_label);
352-
void MCScriptEmitJumpIfTrueInModule(MCScriptModuleBuilderRef builder, uindex_t value_reg, uindex_t target_label);
353-
void MCScriptEmitAssignConstantInModule(MCScriptModuleBuilderRef builder, uindex_t dst_reg, uindex_t const_idx);
354-
void MCScriptEmitAssignInModule(MCScriptModuleBuilderRef builder, uindex_t dst_reg, uindex_t src_reg);
355-
void MCScriptEmitBeginAssignListInModule(MCScriptModuleBuilderRef builder, uindex_t reg);
356-
void MCScriptEmitContinueAssignListInModule(MCScriptModuleBuilderRef builder, uindex_t reg);
357-
void MCScriptEmitEndAssignListInModule(MCScriptModuleBuilderRef builder);
358-
void MCScriptEmitBeginAssignArrayInModule(MCScriptModuleBuilderRef builder, uindex_t reg);
359-
void MCScriptEmitContinueAssignArrayInModule(MCScriptModuleBuilderRef builder, uindex_t reg);
360-
void MCScriptEmitEndAssignArrayInModule(MCScriptModuleBuilderRef builder);
361-
void MCScriptEmitReturnInModule(MCScriptModuleBuilderRef builder, uindex_t reg);
362-
void MCScriptEmitReturnUndefinedInModule(MCScriptModuleBuilderRef builder);
363-
void MCScriptBeginInvokeInModule(MCScriptModuleBuilderRef builder, uindex_t handler_index, uindex_t result_reg);
364-
void MCScriptBeginInvokeIndirectInModule(MCScriptModuleBuilderRef builder, uindex_t handler_reg, uindex_t result_reg);
365-
void MCScriptContinueInvokeInModule(MCScriptModuleBuilderRef builder, uindex_t arg_reg);
366-
void MCScriptEndInvokeInModule(MCScriptModuleBuilderRef builder);
367-
void MCScriptEmitFetchInModule(MCScriptModuleBuilderRef builder, uindex_t dst_reg, uindex_t index, uindex_t level);
368-
void MCScriptEmitStoreInModule(MCScriptModuleBuilderRef builder, uindex_t src_reg, uindex_t index, uindex_t level);
369-
void MCScriptEmitResetInModule(MCScriptModuleBuilderRef builder, uindex_t reg);
370-
371-
void MCScriptEmitPositionInModule(MCScriptModuleBuilderRef builder, MCNameRef file, uindex_t line);
374+
375+
void MCScriptEmitBytecodeInModule(MCScriptModuleBuilderRef builder, uindex_t opcode, ...);
376+
void MCScriptEmitBytecodeInModuleV(MCScriptModuleBuilderRef builder, uindex_t opcode, va_list args);
377+
void MCScriptEmitBytecodeInModuleA(MCScriptModuleBuilderRef builder, uindex_t opcode, uindex_t *arguments, uindex_t argument_count);
378+
379+
void MCScriptEmitPositionForBytecodeInModule(MCScriptModuleBuilderRef builder, MCNameRef file, uindex_t line);
372380

373381
////////////////////////////////////////////////////////////////////////////////
374382

0 commit comments

Comments
 (0)