From a7fafce265908df2eed1923560e4adf73bb0834c Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sat, 28 Mar 2015 09:56:07 -0700 Subject: [PATCH] Pull out opcode definitions into an X-macro file. --- project/xcode/wren.xcodeproj/project.pbxproj | 2 + src/vm/wren_opcodes.h | 192 +++++++++++++++++++ src/vm/wren_vm.c | 77 +------- src/vm/wren_vm.h | 186 +----------------- 4 files changed, 200 insertions(+), 257 deletions(-) create mode 100644 src/vm/wren_opcodes.h diff --git a/project/xcode/wren.xcodeproj/project.pbxproj b/project/xcode/wren.xcodeproj/project.pbxproj index 6c693bba..488c0998 100644 --- a/project/xcode/wren.xcodeproj/project.pbxproj +++ b/project/xcode/wren.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 29205CA61AB4E65E0073018D /* wren_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_utils.h; path = ../../src/vm/wren_utils.h; sourceTree = ""; }; 29205CA71AB4E65E0073018D /* wren_value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_value.h; path = ../../src/vm/wren_value.h; sourceTree = ""; }; 29205CA81AB4E65E0073018D /* wren_vm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_vm.h; path = ../../src/vm/wren_vm.h; sourceTree = ""; }; + 296371B31AC713D000079FDA /* wren_opcodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_opcodes.h; path = ../../src/vm/wren_opcodes.h; sourceTree = ""; }; 29AB1F061816E3AD004B501E /* wren */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wren; sourceTree = BUILT_PRODUCTS_DIR; }; 29C8A92E1AB71C1C00DEC81D /* io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = io.c; path = ../../src/cli/io.c; sourceTree = ""; }; 29C8A9301AB71C3300DEC81D /* io.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = io.h; path = ../../src/cli/io.h; sourceTree = ""; }; @@ -84,6 +85,7 @@ 29205C951AB4E6430073018D /* wren_io.c */, 29DE39521AC3A50A00987D41 /* wren_meta.h */, 29DE39511AC3A50A00987D41 /* wren_meta.c */, + 296371B31AC713D000079FDA /* wren_opcodes.h */, 29205CA61AB4E65E0073018D /* wren_utils.h */, 29205C961AB4E6430073018D /* wren_utils.c */, 29205CA71AB4E65E0073018D /* wren_value.h */, diff --git a/src/vm/wren_opcodes.h b/src/vm/wren_opcodes.h new file mode 100644 index 00000000..bdcaea31 --- /dev/null +++ b/src/vm/wren_opcodes.h @@ -0,0 +1,192 @@ +// This defines the bytecode instructions used by the VM. It does so by invoking +// an OPCODE() macro which is expected to be defined at the point that this is +// included. See: http://en.wikipedia.org/wiki/X_Macro. +// +// Note that the order of instructions here affects the order of the dispatch +// table in the VM's interpreter loop. That in turn affects caching which +// affects overall performance. Take care to run benchmarks if you change the +// order here. + +// Load the constant at index [arg]. +OPCODE(CONSTANT) + +// Push null onto the stack. +OPCODE(NULL) + +// Push false onto the stack. +OPCODE(FALSE) + +// Push true onto the stack. +OPCODE(TRUE) + +// Pushes the value in the given local slot. +OPCODE(LOAD_LOCAL_0) +OPCODE(LOAD_LOCAL_1) +OPCODE(LOAD_LOCAL_2) +OPCODE(LOAD_LOCAL_3) +OPCODE(LOAD_LOCAL_4) +OPCODE(LOAD_LOCAL_5) +OPCODE(LOAD_LOCAL_6) +OPCODE(LOAD_LOCAL_7) +OPCODE(LOAD_LOCAL_8) + +// Note: The compiler assumes the following _STORE instructions always +// immediately follow their corresponding _LOAD ones. + +// Pushes the value in local slot [arg]. +OPCODE(LOAD_LOCAL) + +// Stores the top of stack in local slot [arg]. Does not pop it. +OPCODE(STORE_LOCAL) + +// Pushes the value in upvalue [arg]. +OPCODE(LOAD_UPVALUE) + +// Stores the top of stack in upvalue [arg]. Does not pop it. +OPCODE(STORE_UPVALUE) + +// Pushes the value of the top-level variable in slot [arg]. +OPCODE(LOAD_MODULE_VAR) + +// Stores the top of stack in top-level variable slot [arg]. Does not pop it. +OPCODE(STORE_MODULE_VAR) + +// Pushes the value of the field in slot [arg] of the receiver of the current +// function. This is used for regular field accesses on "this" directly in +// methods. This instruction is faster than the more general CODE_LOAD_FIELD +// instruction. +OPCODE(LOAD_FIELD_THIS) + +// Stores the top of the stack in field slot [arg] in the receiver of the +// current value. Does not pop the value. This instruction is faster than the +// more general CODE_LOAD_FIELD instruction. +OPCODE(STORE_FIELD_THIS) + +// Pops an instance and pushes the value of the field in slot [arg] of it. +OPCODE(LOAD_FIELD) + +// Pops an instance and stores the subsequent top of stack in field slot +// [arg] in it. Does not pop the value. +OPCODE(STORE_FIELD) + +// Pop and discard the top of stack. +OPCODE(POP) + +// Push a copy of the value currently on the top of the stack. +OPCODE(DUP) + +// Invoke the method with symbol [arg]. The number indicates the number of +// arguments (not including the receiver). +OPCODE(CALL_0) +OPCODE(CALL_1) +OPCODE(CALL_2) +OPCODE(CALL_3) +OPCODE(CALL_4) +OPCODE(CALL_5) +OPCODE(CALL_6) +OPCODE(CALL_7) +OPCODE(CALL_8) +OPCODE(CALL_9) +OPCODE(CALL_10) +OPCODE(CALL_11) +OPCODE(CALL_12) +OPCODE(CALL_13) +OPCODE(CALL_14) +OPCODE(CALL_15) +OPCODE(CALL_16) + +// Invoke a superclass method with symbol [arg]. The number indicates the +// number of arguments (not including the receiver). +OPCODE(SUPER_0) +OPCODE(SUPER_1) +OPCODE(SUPER_2) +OPCODE(SUPER_3) +OPCODE(SUPER_4) +OPCODE(SUPER_5) +OPCODE(SUPER_6) +OPCODE(SUPER_7) +OPCODE(SUPER_8) +OPCODE(SUPER_9) +OPCODE(SUPER_10) +OPCODE(SUPER_11) +OPCODE(SUPER_12) +OPCODE(SUPER_13) +OPCODE(SUPER_14) +OPCODE(SUPER_15) +OPCODE(SUPER_16) + +// Jump the instruction pointer [arg] forward. +OPCODE(JUMP) + +// Jump the instruction pointer [arg] backward. Pop and discard the top of +// the stack. +OPCODE(LOOP) + +// Pop and if not truthy then jump the instruction pointer [arg] forward. +OPCODE(JUMP_IF) + +// If the top of the stack is false, jump [arg] forward. Otherwise, pop and +// continue. +OPCODE(AND) + +// If the top of the stack is non-false, jump [arg] forward. Otherwise, pop +// and continue. +OPCODE(OR) + +// Pop [a] then [b] and push true if [b] is an instance of [a]. +OPCODE(IS) + +// Close the upvalue for the local on the top of the stack, then pop it. +OPCODE(CLOSE_UPVALUE) + +// Exit from the current function and return the value on the top of the +// stack. +OPCODE(RETURN) + +// Creates a closure for the function stored at [arg] in the constant table. +// +// Following the function argument is a number of arguments, two for each +// upvalue. The first is true if the variable being captured is a local (as +// opposed to an upvalue), and the second is the index of the local or +// upvalue being captured. +// +// Pushes the created closure. +OPCODE(CLOSURE) + +// Creates a class. Top of stack is the superclass, or `null` if the class +// inherits Object. Below that is a string for the name of the class. Byte +// [arg] is the number of fields in the class. +OPCODE(CLASS) + +// Define a method for symbol [arg]. The class receiving the method is popped +// off the stack, then the function defining the body is popped. +// +// If a foreign method is being defined, the "function" will be a string +// identifying the foreign method. Otherwise, it will be a function or +// closure. +OPCODE(METHOD_INSTANCE) + +// Define a method for symbol [arg]. The class whose metaclass will receive +// the method is popped off the stack, then the function defining the body is +// popped. +// +// If a foreign method is being defined, the "function" will be a string +// identifying the foreign method. Otherwise, it will be a function or +// closure. +OPCODE(METHOD_STATIC) + +// Load the module whose name is stored in string constant [arg]. Pushes +// NULL onto the stack. If the module has already been loaded, does nothing +// else. Otherwise, it creates a fiber to run the desired module and switches +// to that. When that fiber is done, the current one is resumed. +OPCODE(LOAD_MODULE) + +// Reads a top-level variable from another module. [arg1] is a string +// constant for the name of the module, and [arg2] is a string constant for +// the variable name. Pushes the variable if found, or generates a runtime +// error otherwise. +OPCODE(IMPORT_VARIABLE) + +// This pseudo-instruction indicates the end of the bytecode. It should +// always be preceded by a `CODE_RETURN`, so is never actually executed. +OPCODE(END) \ No newline at end of file diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 61031f9c..281d49c9 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -658,80 +658,9 @@ static bool runInterpreter(WrenVM* vm) // Note that the order of instructions here must exacly match the Code enum // in wren_vm.h or horrendously bad things happen. static void* dispatchTable[] = { - &&code_CONSTANT, - &&code_NULL, - &&code_FALSE, - &&code_TRUE, - &&code_LOAD_LOCAL_0, - &&code_LOAD_LOCAL_1, - &&code_LOAD_LOCAL_2, - &&code_LOAD_LOCAL_3, - &&code_LOAD_LOCAL_4, - &&code_LOAD_LOCAL_5, - &&code_LOAD_LOCAL_6, - &&code_LOAD_LOCAL_7, - &&code_LOAD_LOCAL_8, - &&code_LOAD_LOCAL, - &&code_STORE_LOCAL, - &&code_LOAD_UPVALUE, - &&code_STORE_UPVALUE, - &&code_LOAD_MODULE_VAR, - &&code_STORE_MODULE_VAR, - &&code_LOAD_FIELD_THIS, - &&code_STORE_FIELD_THIS, - &&code_LOAD_FIELD, - &&code_STORE_FIELD, - &&code_POP, - &&code_DUP, - &&code_CALL_0, - &&code_CALL_1, - &&code_CALL_2, - &&code_CALL_3, - &&code_CALL_4, - &&code_CALL_5, - &&code_CALL_6, - &&code_CALL_7, - &&code_CALL_8, - &&code_CALL_9, - &&code_CALL_10, - &&code_CALL_11, - &&code_CALL_12, - &&code_CALL_13, - &&code_CALL_14, - &&code_CALL_15, - &&code_CALL_16, - &&code_SUPER_0, - &&code_SUPER_1, - &&code_SUPER_2, - &&code_SUPER_3, - &&code_SUPER_4, - &&code_SUPER_5, - &&code_SUPER_6, - &&code_SUPER_7, - &&code_SUPER_8, - &&code_SUPER_9, - &&code_SUPER_10, - &&code_SUPER_11, - &&code_SUPER_12, - &&code_SUPER_13, - &&code_SUPER_14, - &&code_SUPER_15, - &&code_SUPER_16, - &&code_JUMP, - &&code_LOOP, - &&code_JUMP_IF, - &&code_AND, - &&code_OR, - &&code_IS, - &&code_CLOSE_UPVALUE, - &&code_RETURN, - &&code_CLOSURE, - &&code_CLASS, - &&code_METHOD_INSTANCE, - &&code_METHOD_STATIC, - &&code_LOAD_MODULE, - &&code_IMPORT_VARIABLE, - &&code_END + #define OPCODE(name) &&code_##name, + #include "wren_opcodes.h" + #undef OPCODE }; #define INTERPRET_LOOP DISPATCH(); diff --git a/src/vm/wren_vm.h b/src/vm/wren_vm.h index 715a9906..eee96211 100644 --- a/src/vm/wren_vm.h +++ b/src/vm/wren_vm.h @@ -12,189 +12,9 @@ typedef enum { - // Load the constant at index [arg]. - CODE_CONSTANT, - - // Push null onto the stack. - CODE_NULL, - - // Push false onto the stack. - CODE_FALSE, - - // Push true onto the stack. - CODE_TRUE, - - // Pushes the value in the given local slot. - CODE_LOAD_LOCAL_0, - CODE_LOAD_LOCAL_1, - CODE_LOAD_LOCAL_2, - CODE_LOAD_LOCAL_3, - CODE_LOAD_LOCAL_4, - CODE_LOAD_LOCAL_5, - CODE_LOAD_LOCAL_6, - CODE_LOAD_LOCAL_7, - CODE_LOAD_LOCAL_8, - - // Note: The compiler assumes the following _STORE instructions always - // immediately follow their corresponding _LOAD ones. - - // Pushes the value in local slot [arg]. - CODE_LOAD_LOCAL, - - // Stores the top of stack in local slot [arg]. Does not pop it. - CODE_STORE_LOCAL, - - // Pushes the value in upvalue [arg]. - CODE_LOAD_UPVALUE, - - // Stores the top of stack in upvalue [arg]. Does not pop it. - CODE_STORE_UPVALUE, - - // Pushes the value of the top-level variable in slot [arg]. - CODE_LOAD_MODULE_VAR, - - // Stores the top of stack in top-level variable slot [arg]. Does not pop it. - CODE_STORE_MODULE_VAR, - - // Pushes the value of the field in slot [arg] of the receiver of the current - // function. This is used for regular field accesses on "this" directly in - // methods. This instruction is faster than the more general CODE_LOAD_FIELD - // instruction. - CODE_LOAD_FIELD_THIS, - - // Stores the top of the stack in field slot [arg] in the receiver of the - // current value. Does not pop the value. This instruction is faster than the - // more general CODE_LOAD_FIELD instruction. - CODE_STORE_FIELD_THIS, - - // Pops an instance and pushes the value of the field in slot [arg] of it. - CODE_LOAD_FIELD, - - // Pops an instance and stores the subsequent top of stack in field slot - // [arg] in it. Does not pop the value. - CODE_STORE_FIELD, - - // Pop and discard the top of stack. - CODE_POP, - - // Push a copy of the value currently on the top of the stack. - CODE_DUP, - - // Invoke the method with symbol [arg]. The number indicates the number of - // arguments (not including the receiver). - CODE_CALL_0, - CODE_CALL_1, - CODE_CALL_2, - CODE_CALL_3, - CODE_CALL_4, - CODE_CALL_5, - CODE_CALL_6, - CODE_CALL_7, - CODE_CALL_8, - CODE_CALL_9, - CODE_CALL_10, - CODE_CALL_11, - CODE_CALL_12, - CODE_CALL_13, - CODE_CALL_14, - CODE_CALL_15, - CODE_CALL_16, - - // Invoke a superclass method with symbol [arg]. The number indicates the - // number of arguments (not including the receiver). - CODE_SUPER_0, - CODE_SUPER_1, - CODE_SUPER_2, - CODE_SUPER_3, - CODE_SUPER_4, - CODE_SUPER_5, - CODE_SUPER_6, - CODE_SUPER_7, - CODE_SUPER_8, - CODE_SUPER_9, - CODE_SUPER_10, - CODE_SUPER_11, - CODE_SUPER_12, - CODE_SUPER_13, - CODE_SUPER_14, - CODE_SUPER_15, - CODE_SUPER_16, - - // Jump the instruction pointer [arg] forward. - CODE_JUMP, - - // Jump the instruction pointer [arg] backward. Pop and discard the top of - // the stack. - CODE_LOOP, - - // Pop and if not truthy then jump the instruction pointer [arg] forward. - CODE_JUMP_IF, - - // If the top of the stack is false, jump [arg] forward. Otherwise, pop and - // continue. - CODE_AND, - - // If the top of the stack is non-false, jump [arg] forward. Otherwise, pop - // and continue. - CODE_OR, - - // Pop [a] then [b] and push true if [b] is an instance of [a]. - CODE_IS, - - // Close the upvalue for the local on the top of the stack, then pop it. - CODE_CLOSE_UPVALUE, - - // Exit from the current function and return the value on the top of the - // stack. - CODE_RETURN, - - // Creates a closure for the function stored at [arg] in the constant table. - // - // Following the function argument is a number of arguments, two for each - // upvalue. The first is true if the variable being captured is a local (as - // opposed to an upvalue), and the second is the index of the local or - // upvalue being captured. - // - // Pushes the created closure. - CODE_CLOSURE, - - // Creates a class. Top of stack is the superclass, or `null` if the class - // inherits Object. Below that is a string for the name of the class. Byte - // [arg] is the number of fields in the class. - CODE_CLASS, - - // Define a method for symbol [arg]. The class receiving the method is popped - // off the stack, then the function defining the body is popped. - // - // If a foreign method is being defined, the "function" will be a string - // identifying the foreign method. Otherwise, it will be a function or - // closure. - CODE_METHOD_INSTANCE, - - // Define a method for symbol [arg]. The class whose metaclass will receive - // the method is popped off the stack, then the function defining the body is - // popped. - // - // If a foreign method is being defined, the "function" will be a string - // identifying the foreign method. Otherwise, it will be a function or - // closure. - CODE_METHOD_STATIC, - - // Load the module whose name is stored in string constant [arg]. Pushes - // NULL onto the stack. If the module has already been loaded, does nothing - // else. Otherwise, it creates a fiber to run the desired module and switches - // to that. When that fiber is done, the current one is resumed. - CODE_LOAD_MODULE, - - // Reads a top-level variable from another module. [arg1] is a string - // constant for the name of the module, and [arg2] is a string constant for - // the variable name. Pushes the variable if found, or generates a runtime - // error otherwise. - CODE_IMPORT_VARIABLE, - - // This pseudo-instruction indicates the end of the bytecode. It should - // always be preceded by a `CODE_RETURN`, so is never actually executed. - CODE_END + #define OPCODE(name) CODE_##name, + #include "wren_opcodes.h" + #undef OPCODE } Code; struct WrenMethod