Created
December 11, 2013 15:58
-
-
Save munificent/7912965 to your computer and use it in GitHub Desktop.
Patch for generating default constructors at compile time. For mysterious reasons, it causes the method_call benchmark perf to degrade by ~10%.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/wren_compiler.c b/src/wren_compiler.c | |
index cbb9ca6..83420f1 100644 | |
--- a/src/wren_compiler.c | |
+++ b/src/wren_compiler.c | |
@@ -1600,6 +1600,24 @@ void block(Compiler* compiler) | |
emit(compiler, CODE_POP); | |
} | |
+// Defines a default constructor method with an empty body. | |
+static void defaultConstructor(Compiler* compiler) | |
+{ | |
+ Compiler ctorCompiler; | |
+ int constant = initCompiler(&ctorCompiler, compiler->parser, compiler, 1); | |
+ | |
+ // Just return the receiver which is in the first local slot. | |
+ emit(&ctorCompiler, CODE_LOAD_LOCAL); | |
+ emit(&ctorCompiler, 0); | |
+ emit(&ctorCompiler, CODE_RETURN); | |
+ | |
+ endCompiler(&ctorCompiler, constant); | |
+ | |
+ // Define the constructor method. | |
+ emit(compiler, CODE_METHOD_CTOR); | |
+ emit(compiler, ensureSymbol(&compiler->parser->vm->methods, "new", 3)); | |
+} | |
+ | |
// Compiles a statement. These can only appear at the top-level or within | |
// curly blocks. Unlike expressions, these do not leave a value on the stack. | |
void statement(Compiler* compiler) | |
@@ -1626,20 +1644,20 @@ void statement(Compiler* compiler) | |
// used. | |
int numFieldsInstruction = emit(compiler, 255); | |
- // Compile the method definitions. | |
- consume(compiler, TOKEN_LEFT_BRACE, "Expect '}' after class body."); | |
- | |
- // Set up a symbol table for the class's fields. | |
+ // Set up a symbol table for the class's fields. We'll initially compile | |
+ // them to slots starting at zero. When the method is bound to the close | |
+ // the bytecode will be adjusted by [wrenBindMethod] to take inherited | |
+ // fields into account. | |
SymbolTable* previousFields = compiler->fields; | |
SymbolTable fields; | |
initSymbolTable(&fields); | |
compiler->fields = &fields; | |
- // TODO(bob): Need to handle inherited fields. Ideally, a subclass's fields | |
- // would be statically compiled to slot indexes right after the superclass | |
- // ones, but we don't know the superclass statically. Instead, will | |
- // probably have to determine the field offset at class creation time in | |
- // the VM and then adjust by that every time a field is accessed/modified. | |
+ // Classes with no explicitly defined constructor get a default one. | |
+ int hasConstructor = 0; | |
+ | |
+ // Compile the method definitions. | |
+ consume(compiler, TOKEN_LEFT_BRACE, "Expect '}' after class body."); | |
while (!match(compiler, TOKEN_RIGHT_BRACE)) | |
{ | |
@@ -1655,6 +1673,7 @@ void statement(Compiler* compiler) | |
{ | |
// If the method name is prefixed with "this", it's a constructor. | |
instruction = CODE_METHOD_CTOR; | |
+ hasConstructor = 1; | |
} | |
SignatureFn signature = rules[compiler->parser->current.type].method; | |
@@ -1671,6 +1690,8 @@ void statement(Compiler* compiler) | |
"Expect newline after definition in class."); | |
} | |
+ if (!hasConstructor) defaultConstructor(compiler); | |
+ | |
// Update the class with the number of fields. | |
compiler->fn->bytecode[numFieldsInstruction] = fields.count; | |
diff --git a/src/wren_vm.c b/src/wren_vm.c | |
index 70156c3..fed2f54 100644 | |
--- a/src/wren_vm.c | |
+++ b/src/wren_vm.c | |
@@ -672,11 +672,9 @@ Value interpret(WrenVM* vm, Value function) | |
vm->objectClass = classObj; | |
} | |
- // Define a "new" method on the metaclass. | |
- // TODO(bob): Can this be inherited? | |
- int newSymbol = ensureSymbol(&vm->methods, "new", strlen("new")); | |
- classObj->metaclass->methods[newSymbol].type = METHOD_CTOR; | |
- classObj->metaclass->methods[newSymbol].fn = NULL_VAL; | |
+ // TODO(bob): If I remove this line, the method_call benchmark runs ~10% | |
+ // slower. WTF? | |
+ ensureSymbol(&vm->methods, "new", 3); | |
PUSH(OBJ_VAL(classObj)); | |
DISPATCH(); | |
@@ -915,19 +913,10 @@ Value interpret(WrenVM* vm, Value function) | |
// "this" in the body of the constructor and returned by it. | |
fiber->stack[fiber->stackSize - numArgs] = instance; | |
- if (IS_NULL(method->fn)) | |
- { | |
- // Default constructor, so no body to call. Just discard the | |
- // stack slots for the arguments (but leave one for the instance). | |
- fiber->stackSize -= numArgs - 1; | |
- } | |
- else | |
- { | |
- // Invoke the constructor body. | |
- STORE_FRAME(); | |
- wrenCallFunction(fiber, method->fn, numArgs); | |
- LOAD_FRAME(); | |
- } | |
+ // Invoke the constructor body. | |
+ STORE_FRAME(); | |
+ wrenCallFunction(fiber, method->fn, numArgs); | |
+ LOAD_FRAME(); | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment