1 module wren.opcodes;
2 
3 // This defines the bytecode instructions used by the VM. 
4 // The first argument is the name of the opcode. The second is its "stack
5 // effect" -- the amount that the op code changes the size of the stack. A
6 // stack effect of 1 means it pushes a value and the stack grows one larger.
7 // -2 means it pops two values, etc.
8 struct WrenOpcode 
9 {
10     string name;
11     int stackEffects;
12 }
13 
14 static immutable WREN_OPCODE_FULL_TABLE =
15 [
16     // Load the constant at index [arg].
17     WrenOpcode("CONSTANT", 1),
18 
19     // Push null onto the stack.
20     WrenOpcode("NULL", 1),
21 
22     // Push false onto the stack.
23     WrenOpcode("FALSE", 1),
24 
25     // Push true onto the stack.
26     WrenOpcode("TRUE", 1),
27 
28     // Pushes the value in the given local slot.
29     WrenOpcode("LOAD_LOCAL_0", 1),
30     WrenOpcode("LOAD_LOCAL_1", 1),
31     WrenOpcode("LOAD_LOCAL_2", 1),
32     WrenOpcode("LOAD_LOCAL_3", 1),
33     WrenOpcode("LOAD_LOCAL_4", 1),
34     WrenOpcode("LOAD_LOCAL_5", 1),
35     WrenOpcode("LOAD_LOCAL_6", 1),
36     WrenOpcode("LOAD_LOCAL_7", 1),
37     WrenOpcode("LOAD_LOCAL_8", 1),
38 
39     // Note: The compiler assumes the following _STORE instructions always
40     // immediately follow their corresponding _LOAD ones.
41 
42     // Pushes the value in local slot [arg].
43     WrenOpcode("LOAD_LOCAL", 1),
44 
45     // Stores the top of stack in local slot [arg]. Does not pop it.
46     WrenOpcode("STORE_LOCAL", 0),
47 
48     // Pushes the value in upvalue [arg].
49     WrenOpcode("LOAD_UPVALUE", 1),
50 
51     // Stores the top of stack in upvalue [arg]. Does not pop it.
52     WrenOpcode("STORE_UPVALUE", 0),
53 
54     // Pushes the value of the top-level variable in slot [arg].
55     WrenOpcode("LOAD_MODULE_VAR", 1),
56 
57     // Stores the top of stack in top-level variable slot [arg]. Does not pop it.
58     WrenOpcode("STORE_MODULE_VAR", 0),
59 
60     // Pushes the value of the field in slot [arg] of the receiver of the current
61     // function. This is used for regular field accesses on "this" directly in
62     // methods. This instruction is faster than the more general CODE_LOAD_FIELD
63     // instruction.
64     WrenOpcode("LOAD_FIELD_THIS", 1),
65 
66     // Stores the top of the stack in field slot [arg] in the receiver of the
67     // current value. Does not pop the value. This instruction is faster than the
68     // more general CODE_LOAD_FIELD instruction.
69     WrenOpcode("STORE_FIELD_THIS", 0),
70 
71     // Pops an instance and pushes the value of the field in slot [arg] of it.
72     WrenOpcode("LOAD_FIELD", 0),
73 
74     // Pops an instance and stores the subsequent top of stack in field slot
75     // [arg] in it. Does not pop the value.
76     WrenOpcode("STORE_FIELD", -1),
77 
78     // Pop and discard the top of stack.
79     WrenOpcode("POP", -1),
80 
81     // Invoke the method with symbol [arg]. The number indicates the number of
82     // arguments (not including the receiver).
83     WrenOpcode("CALL_0", 0),
84     WrenOpcode("CALL_1", -1),
85     WrenOpcode("CALL_2", -2),
86     WrenOpcode("CALL_3", -3),
87     WrenOpcode("CALL_4", -4),
88     WrenOpcode("CALL_5", -5),
89     WrenOpcode("CALL_6", -6),
90     WrenOpcode("CALL_7", -7),
91     WrenOpcode("CALL_8", -8),
92     WrenOpcode("CALL_9", -9),
93     WrenOpcode("CALL_10", -10),
94     WrenOpcode("CALL_11", -11),
95     WrenOpcode("CALL_12", -12),
96     WrenOpcode("CALL_13", -13),
97     WrenOpcode("CALL_14", -14),
98     WrenOpcode("CALL_15", -15),
99     WrenOpcode("CALL_16", -16),
100 
101     // Invoke a superclass method with symbol [arg]. The number indicates the
102     // number of arguments (not including the receiver).
103     WrenOpcode("SUPER_0", 0),
104     WrenOpcode("SUPER_1", -1),
105     WrenOpcode("SUPER_2", -2),
106     WrenOpcode("SUPER_3", -3),
107     WrenOpcode("SUPER_4", -4),
108     WrenOpcode("SUPER_5", -5),
109     WrenOpcode("SUPER_6", -6),
110     WrenOpcode("SUPER_7", -7),
111     WrenOpcode("SUPER_8", -8),
112     WrenOpcode("SUPER_9", -9),
113     WrenOpcode("SUPER_10", -10),
114     WrenOpcode("SUPER_11", -11),
115     WrenOpcode("SUPER_12", -12),
116     WrenOpcode("SUPER_13", -13),
117     WrenOpcode("SUPER_14", -14),
118     WrenOpcode("SUPER_15", -15),
119     WrenOpcode("SUPER_16", -16),
120 
121     // Jump the instruction pointer [arg] forward.
122     WrenOpcode("JUMP", 0),
123 
124     // Jump the instruction pointer [arg] backward.
125     WrenOpcode("LOOP", 0),
126 
127     // Pop and if not truthy then jump the instruction pointer [arg] forward.
128     WrenOpcode("JUMP_IF", -1),
129 
130     // If the top of the stack is false, jump [arg] forward. Otherwise, pop and
131     // continue.
132     WrenOpcode("AND", -1),
133 
134     // If the top of the stack is non-false, jump [arg] forward. Otherwise, pop
135     // and continue.
136     WrenOpcode("OR", -1),
137 
138     // Close the upvalue for the local on the top of the stack, then pop it.
139     WrenOpcode("CLOSE_UPVALUE", -1),
140 
141     // Exit from the current function and return the value on the top of the
142     // stack.
143     WrenOpcode("RETURN", 0),
144 
145     // Creates a closure for the function stored at [arg] in the constant table.
146     //
147     // Following the function argument is a number of arguments, two for each
148     // upvalue. The first is true if the variable being captured is a local (as
149     // opposed to an upvalue), and the second is the index of the local or
150     // upvalue being captured.
151     //
152     // Pushes the created closure.
153     WrenOpcode("CLOSURE", 1),
154 
155     // Creates a new instance of a class.
156     //
157     // Assumes the class object is in slot zero, and replaces it with the new
158     // uninitialized instance of that class. This opcode is only emitted by the
159     // compiler-generated constructor metaclass methods.
160     WrenOpcode("CONSTRUCT", 0),
161 
162     // Creates a new instance of a foreign class.
163     //
164     // Assumes the class object is in slot zero, and replaces it with the new
165     // uninitialized instance of that class. This opcode is only emitted by the
166     // compiler-generated constructor metaclass methods.
167     WrenOpcode("FOREIGN_CONSTRUCT", 0),
168 
169     // Creates a class. Top of stack is the superclass. Below that is a string for
170     // the name of the class. Byte [arg] is the number of fields in the class.
171     WrenOpcode("CLASS", -1),
172 
173     // Ends a class. 
174     // Atm the stack contains the class and the ClassAttributes (or null).
175     WrenOpcode("END_CLASS", -2),
176 
177     // Creates a foreign class. Top of stack is the superclass. Below that is a
178     // string for the name of the class.
179     WrenOpcode("FOREIGN_CLASS", -1),
180 
181     // Define a method for symbol [arg]. The class receiving the method is popped
182     // off the stack, then the function defining the body is popped.
183     //
184     // If a foreign method is being defined, the "function" will be a string
185     // identifying the foreign method. Otherwise, it will be a function or
186     // closure.
187     WrenOpcode("METHOD_INSTANCE", -2),
188 
189     // Define a method for symbol [arg]. The class whose metaclass will receive
190     // the method is popped off the stack, then the function defining the body is
191     // popped.
192     //
193     // If a foreign method is being defined, the "function" will be a string
194     // identifying the foreign method. Otherwise, it will be a function or
195     // closure.
196     WrenOpcode("METHOD_STATIC", -2),
197 
198     // This is executed at the end of the module's body. Pushes NULL onto the stack
199     // as the "return value" of the import statement and stores the module as the
200     // most recently imported one.
201     WrenOpcode("END_MODULE", 1),
202 
203     // Import a module whose name is the string stored at [arg] in the constant
204     // table.
205     //
206     // Pushes null onto the stack so that the fiber for the imported module can
207     // replace that with a dummy value when it returns. (Fibers always return a
208     // value when resuming a caller.)
209     WrenOpcode("IMPORT_MODULE", 1),
210 
211     // Import a variable from the most recently imported module. The name of the
212     // variable to import is at [arg] in the constant table. Pushes the loaded
213     // variable's value.
214     WrenOpcode("IMPORT_VARIABLE", 1),
215 
216     // This pseudo-instruction indicates the end of the bytecode. It should
217     // always be preceded by a `CODE_RETURN`, so is never actually executed.
218     WrenOpcode("END", 0),
219 ];
220 
221 // Generate the enum table for opcodes
222 mixin (() {
223     string str = "enum Code {";
224     static foreach(op; WREN_OPCODE_FULL_TABLE) {
225         str ~= "CODE_" ~ op.name ~ ", ";
226     }
227     str ~= "}";
228     return str;
229 }());
230 
231 // Generate the stack effects table for opcodes
232 mixin (() {
233     import std.conv : to;
234     string str = "static immutable stackEffects = [";
235     static foreach(op; WREN_OPCODE_FULL_TABLE) {
236         str ~= to!string(op.stackEffects) ~ ", ";
237     }
238     str ~= "];";
239     return str;
240 }());