From ac67386208295282ab483a1b5a1d403c4cdfdf1a Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sat, 6 Dec 2014 19:59:11 -0800 Subject: [PATCH] Add specialized instructions for loading some locals. Not having to decode a separate arg byte improves some benchmarks: binary_trees - wren .......... 3094 0.32s 101.95% relative to baseline delta_blue - wren .......... 6555 0.15s 101.05% relative to baseline fib - wren .......... 2326 0.43s 107.32% relative to baseline for - wren .......... 6183 0.16s 102.81% relative to baseline method_call - wren .......... 4746 0.21s 105.97% relative to baseline --- doc/instruction counts.txt | 65 ++++++++++++++++++++++++++++++++ src/wren_compiler.c | 45 ++++++++++++++++++---- src/wren_debug.c | 10 +++++ src/wren_vm.c | 77 +++++++++++++++++++++++++------------- src/wren_vm.h | 11 ++++++ 5 files changed, 174 insertions(+), 34 deletions(-) create mode 100644 doc/instruction counts.txt diff --git a/doc/instruction counts.txt b/doc/instruction counts.txt new file mode 100644 index 00000000..3d4eac42 --- /dev/null +++ b/doc/instruction counts.txt @@ -0,0 +1,65 @@ +This is the number of times each instruction was executed when running the +delta_blue benchmark: + +3753021 CODE_LOAD_LOCAL +2233991 CODE_RETURN +2151580 CODE_LOAD_FIELD_THIS +2121398 CODE_CALL_1 +1827535 CODE_CALL_0 +1328364 CODE_POP +1136064 CODE_JUMP_IF +715071 CODE_LOAD_GLOBAL +428374 CODE_STORE_FIELD_THIS +424999 CODE_NULL +355344 CODE_STORE_LOCAL +341762 CODE_LOOP +118855 CODE_CONSTANT +93048 CODE_CALL_2 +75280 CODE_AND +59920 CODE_JUMP +16842 CODE_LIST +16660 CODE_TRUE +10040 CODE_OR +8200 CODE_LOAD_UPVALUE +8140 CODE_SUPER_1 +6540 CODE_FALSE +6076 CODE_STORE_GLOBAL +4000 CODE_SUPER_3 +2020 CODE_SUPER_2 +2000 CODE_SUPER_0 +2000 CODE_CALL_5 +2000 CODE_CALL_3 +160 CODE_CLOSURE +74 CODE_METHOD_INSTANCE +11 CODE_CLASS +4 CODE_METHOD_STATIC +0 CODE_SUPER_9 +0 CODE_SUPER_8 +0 CODE_SUPER_7 +0 CODE_SUPER_6 +0 CODE_SUPER_5 +0 CODE_SUPER_4 +0 CODE_SUPER_16 +0 CODE_SUPER_15 +0 CODE_SUPER_14 +0 CODE_SUPER_13 +0 CODE_SUPER_12 +0 CODE_SUPER_11 +0 CODE_SUPER_10 +0 CODE_STORE_UPVALUE +0 CODE_STORE_FIELD +0 CODE_LOAD_FIELD +0 CODE_IS +0 CODE_CLOSE_UPVALUE +0 CODE_CALL_9 +0 CODE_CALL_8 +0 CODE_CALL_7 +0 CODE_CALL_6 +0 CODE_CALL_4 +0 CODE_CALL_16 +0 CODE_CALL_15 +0 CODE_CALL_14 +0 CODE_CALL_13 +0 CODE_CALL_12 +0 CODE_CALL_11 +0 CODE_CALL_10 \ No newline at end of file diff --git a/src/wren_compiler.c b/src/wren_compiler.c index 3025f39c..bbc73134 100644 --- a/src/wren_compiler.c +++ b/src/wren_compiler.c @@ -1168,6 +1168,17 @@ static int resolveName(Compiler* compiler, const char* name, int length, &compiler->parser->vm->globalNames, name, length); } +static void loadLocal(Compiler* compiler, int slot) +{ + if (slot <= 8) + { + emit(compiler, CODE_LOAD_LOCAL_0 + slot); + return; + } + + emitByte(compiler, CODE_LOAD_LOCAL, slot); +} + // Copies the identifier from the previously consumed `TOKEN_NAME` into [name], // which should point to a buffer large enough to contain it. Returns the // length of the name. @@ -1363,7 +1374,7 @@ static void finishBody(Compiler* compiler, bool isConstructor) if (!isStatementBody) emit(compiler, CODE_POP); // The receiver is always stored in the first local slot. - emitByte(compiler, CODE_LOAD_LOCAL, 0); + emit(compiler, CODE_LOAD_LOCAL_0); } else if (isStatementBody) { @@ -1511,7 +1522,14 @@ static void loadThis(Compiler* compiler) int index = resolveName(compiler, "this", 4, &loadInstruction); ASSERT(index == -1 || loadInstruction != CODE_LOAD_GLOBAL, "'this' should not be global."); - emitByte(compiler, loadInstruction, index); + if (loadInstruction == CODE_LOAD_LOCAL) + { + loadLocal(compiler, index); + } + else + { + emitByte(compiler, loadInstruction, index); + } } static void grouping(Compiler* compiler, bool allowAssignment) @@ -1671,6 +1689,10 @@ static void variable(Compiler* compiler, bool allowAssignment, int index, { emitShort(compiler, loadInstruction, index); } + else if (loadInstruction == CODE_LOAD_LOCAL) + { + loadLocal(compiler, index); + } else { emitByte(compiler, loadInstruction, index); @@ -2187,6 +2209,15 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants, case CODE_CLOSE_UPVALUE: case CODE_RETURN: case CODE_END: + case CODE_LOAD_LOCAL_0: + case CODE_LOAD_LOCAL_1: + case CODE_LOAD_LOCAL_2: + case CODE_LOAD_LOCAL_3: + case CODE_LOAD_LOCAL_4: + case CODE_LOAD_LOCAL_5: + case CODE_LOAD_LOCAL_6: + case CODE_LOAD_LOCAL_7: + case CODE_LOAD_LOCAL_8: return 0; case CODE_LOAD_LOCAL: @@ -2379,8 +2410,8 @@ static void forStatement(Compiler* compiler) startLoop(compiler, &loop); // Advance the iterator by calling the ".iterate" method on the sequence. - emitByte(compiler, CODE_LOAD_LOCAL, seqSlot); - emitByte(compiler, CODE_LOAD_LOCAL, iterSlot); + loadLocal(compiler, seqSlot); + loadLocal(compiler, iterSlot); emitShort(compiler, CODE_CALL_1, methodSymbol(compiler, "iterate ", 8)); @@ -2391,8 +2422,8 @@ static void forStatement(Compiler* compiler) testExitLoop(compiler); // Get the current value in the sequence by calling ".iteratorValue". - emitByte(compiler, CODE_LOAD_LOCAL, seqSlot); - emitByte(compiler, CODE_LOAD_LOCAL, iterSlot); + loadLocal(compiler, seqSlot); + loadLocal(compiler, iterSlot); emitShort(compiler, CODE_CALL_1, methodSymbol(compiler, "iteratorValue ", 14)); @@ -2630,7 +2661,7 @@ static void classDefinition(Compiler* compiler) } else { - emitByte(compiler, CODE_LOAD_LOCAL, symbol); + loadLocal(compiler, symbol); } // Define the method. diff --git a/src/wren_debug.c b/src/wren_debug.c index 0d25a1d7..6c738ab5 100644 --- a/src/wren_debug.c +++ b/src/wren_debug.c @@ -67,6 +67,16 @@ static int debugPrintInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine) case CODE_FALSE: printf("FALSE\n"); break; case CODE_TRUE: printf("TRUE\n"); break; + case CODE_LOAD_LOCAL_0: printf("LOAD_LOCAL_0\n"); break; + case CODE_LOAD_LOCAL_1: printf("LOAD_LOCAL_1\n"); break; + case CODE_LOAD_LOCAL_2: printf("LOAD_LOCAL_2\n"); break; + case CODE_LOAD_LOCAL_3: printf("LOAD_LOCAL_3\n"); break; + case CODE_LOAD_LOCAL_4: printf("LOAD_LOCAL_4\n"); break; + case CODE_LOAD_LOCAL_5: printf("LOAD_LOCAL_5\n"); break; + case CODE_LOAD_LOCAL_6: printf("LOAD_LOCAL_6\n"); break; + case CODE_LOAD_LOCAL_7: printf("LOAD_LOCAL_7\n"); break; + case CODE_LOAD_LOCAL_8: printf("LOAD_LOCAL_8\n"); break; + case CODE_LOAD_LOCAL: BYTE_INSTRUCTION("LOAD_LOCAL"); case CODE_STORE_LOCAL: BYTE_INSTRUCTION("STORE_LOCAL"); case CODE_LOAD_UPVALUE: BYTE_INSTRUCTION("LOAD_UPVALUE"); diff --git a/src/wren_vm.c b/src/wren_vm.c index 51bbe601..9d99fa32 100644 --- a/src/wren_vm.c +++ b/src/wren_vm.c @@ -418,6 +418,15 @@ static bool runInterpreter(WrenVM* vm) &&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, @@ -482,18 +491,20 @@ static bool runInterpreter(WrenVM* vm) #define INTERPRET_LOOP DISPATCH(); #define CASE_CODE(name) code_##name - #if WREN_DEBUG_TRACE_INSTRUCTIONS - // Prints the stack and instruction before each instruction is executed. - #define DISPATCH() \ - { \ - wrenDebugPrintStack(fiber); \ - wrenDebugPrintInstruction(vm, fn, (int)(ip - fn->bytecode)); \ - instruction = *ip++; \ - goto *dispatchTable[instruction]; \ - } - #else - #define DISPATCH() goto *dispatchTable[instruction = READ_BYTE()] - #endif + #if WREN_DEBUG_TRACE_INSTRUCTIONS + // Prints the stack and instruction before each instruction is executed. + #define DISPATCH() \ + { \ + wrenDebugPrintStack(fiber); \ + wrenDebugPrintInstruction(vm, fn, (int)(ip - fn->bytecode)); \ + instruction = *ip++; \ + goto *dispatchTable[instruction]; \ + } + #else + + #define DISPATCH() goto *dispatchTable[instruction = READ_BYTE()]; + + #endif #else @@ -508,6 +519,33 @@ static bool runInterpreter(WrenVM* vm) Code instruction; INTERPRET_LOOP { + CASE_CODE(LOAD_LOCAL_0): + CASE_CODE(LOAD_LOCAL_1): + CASE_CODE(LOAD_LOCAL_2): + CASE_CODE(LOAD_LOCAL_3): + CASE_CODE(LOAD_LOCAL_4): + CASE_CODE(LOAD_LOCAL_5): + CASE_CODE(LOAD_LOCAL_6): + CASE_CODE(LOAD_LOCAL_7): + CASE_CODE(LOAD_LOCAL_8): + PUSH(fiber->stack[frame->stackStart + instruction - CODE_LOAD_LOCAL_0]); + DISPATCH(); + + CASE_CODE(LOAD_LOCAL): + PUSH(fiber->stack[frame->stackStart + READ_BYTE()]); + DISPATCH(); + + CASE_CODE(LOAD_FIELD_THIS): + { + int field = READ_BYTE(); + Value receiver = fiber->stack[frame->stackStart]; + ASSERT(IS_INSTANCE(receiver), "Receiver should be instance."); + ObjInstance* instance = AS_INSTANCE(receiver); + ASSERT(field < instance->classObj->numFields, "Out of bounds field."); + PUSH(instance->fields[field]); + DISPATCH(); + } + CASE_CODE(POP): DROP(); DISPATCH(); CASE_CODE(NULL): PUSH(NULL_VAL); DISPATCH(); CASE_CODE(FALSE): PUSH(FALSE_VAL); DISPATCH(); @@ -604,10 +642,6 @@ static bool runInterpreter(WrenVM* vm) DISPATCH(); } - CASE_CODE(LOAD_LOCAL): - PUSH(fiber->stack[frame->stackStart + READ_BYTE()]); - DISPATCH(); - CASE_CODE(STORE_LOCAL): fiber->stack[frame->stackStart + READ_BYTE()] = PEEK(); DISPATCH(); @@ -730,17 +764,6 @@ static bool runInterpreter(WrenVM* vm) vm->globals.data[READ_SHORT()] = PEEK(); DISPATCH(); - CASE_CODE(LOAD_FIELD_THIS): - { - int field = READ_BYTE(); - Value receiver = fiber->stack[frame->stackStart]; - ASSERT(IS_INSTANCE(receiver), "Receiver should be instance."); - ObjInstance* instance = AS_INSTANCE(receiver); - ASSERT(field < instance->classObj->numFields, "Out of bounds field."); - PUSH(instance->fields[field]); - DISPATCH(); - } - CASE_CODE(STORE_FIELD_THIS): { int field = READ_BYTE(); diff --git a/src/wren_vm.h b/src/wren_vm.h index b8e7a262..ed10a5bb 100644 --- a/src/wren_vm.h +++ b/src/wren_vm.h @@ -40,6 +40,17 @@ typedef enum // 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, + // Pushes the value in local slot [arg]. CODE_LOAD_LOCAL,