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 }());