forked from Mirror/wren
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
This commit is contained in:
65
doc/instruction counts.txt
Normal file
65
doc/instruction counts.txt
Normal file
@ -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
|
||||||
@ -1168,6 +1168,17 @@ static int resolveName(Compiler* compiler, const char* name, int length,
|
|||||||
&compiler->parser->vm->globalNames, name, 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],
|
// Copies the identifier from the previously consumed `TOKEN_NAME` into [name],
|
||||||
// which should point to a buffer large enough to contain it. Returns the
|
// which should point to a buffer large enough to contain it. Returns the
|
||||||
// length of the name.
|
// length of the name.
|
||||||
@ -1363,7 +1374,7 @@ static void finishBody(Compiler* compiler, bool isConstructor)
|
|||||||
if (!isStatementBody) emit(compiler, CODE_POP);
|
if (!isStatementBody) emit(compiler, CODE_POP);
|
||||||
|
|
||||||
// The receiver is always stored in the first local slot.
|
// 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)
|
else if (isStatementBody)
|
||||||
{
|
{
|
||||||
@ -1511,7 +1522,14 @@ static void loadThis(Compiler* compiler)
|
|||||||
int index = resolveName(compiler, "this", 4, &loadInstruction);
|
int index = resolveName(compiler, "this", 4, &loadInstruction);
|
||||||
ASSERT(index == -1 || loadInstruction != CODE_LOAD_GLOBAL,
|
ASSERT(index == -1 || loadInstruction != CODE_LOAD_GLOBAL,
|
||||||
"'this' should not be 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)
|
static void grouping(Compiler* compiler, bool allowAssignment)
|
||||||
@ -1671,6 +1689,10 @@ static void variable(Compiler* compiler, bool allowAssignment, int index,
|
|||||||
{
|
{
|
||||||
emitShort(compiler, loadInstruction, index);
|
emitShort(compiler, loadInstruction, index);
|
||||||
}
|
}
|
||||||
|
else if (loadInstruction == CODE_LOAD_LOCAL)
|
||||||
|
{
|
||||||
|
loadLocal(compiler, index);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emitByte(compiler, loadInstruction, index);
|
emitByte(compiler, loadInstruction, index);
|
||||||
@ -2187,6 +2209,15 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
|||||||
case CODE_CLOSE_UPVALUE:
|
case CODE_CLOSE_UPVALUE:
|
||||||
case CODE_RETURN:
|
case CODE_RETURN:
|
||||||
case CODE_END:
|
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;
|
return 0;
|
||||||
|
|
||||||
case CODE_LOAD_LOCAL:
|
case CODE_LOAD_LOCAL:
|
||||||
@ -2379,8 +2410,8 @@ static void forStatement(Compiler* compiler)
|
|||||||
startLoop(compiler, &loop);
|
startLoop(compiler, &loop);
|
||||||
|
|
||||||
// Advance the iterator by calling the ".iterate" method on the sequence.
|
// Advance the iterator by calling the ".iterate" method on the sequence.
|
||||||
emitByte(compiler, CODE_LOAD_LOCAL, seqSlot);
|
loadLocal(compiler, seqSlot);
|
||||||
emitByte(compiler, CODE_LOAD_LOCAL, iterSlot);
|
loadLocal(compiler, iterSlot);
|
||||||
|
|
||||||
emitShort(compiler, CODE_CALL_1, methodSymbol(compiler, "iterate ", 8));
|
emitShort(compiler, CODE_CALL_1, methodSymbol(compiler, "iterate ", 8));
|
||||||
|
|
||||||
@ -2391,8 +2422,8 @@ static void forStatement(Compiler* compiler)
|
|||||||
testExitLoop(compiler);
|
testExitLoop(compiler);
|
||||||
|
|
||||||
// Get the current value in the sequence by calling ".iteratorValue".
|
// Get the current value in the sequence by calling ".iteratorValue".
|
||||||
emitByte(compiler, CODE_LOAD_LOCAL, seqSlot);
|
loadLocal(compiler, seqSlot);
|
||||||
emitByte(compiler, CODE_LOAD_LOCAL, iterSlot);
|
loadLocal(compiler, iterSlot);
|
||||||
|
|
||||||
emitShort(compiler, CODE_CALL_1,
|
emitShort(compiler, CODE_CALL_1,
|
||||||
methodSymbol(compiler, "iteratorValue ", 14));
|
methodSymbol(compiler, "iteratorValue ", 14));
|
||||||
@ -2630,7 +2661,7 @@ static void classDefinition(Compiler* compiler)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emitByte(compiler, CODE_LOAD_LOCAL, symbol);
|
loadLocal(compiler, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the method.
|
// Define the method.
|
||||||
|
|||||||
@ -67,6 +67,16 @@ static int debugPrintInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
|||||||
case CODE_FALSE: printf("FALSE\n"); break;
|
case CODE_FALSE: printf("FALSE\n"); break;
|
||||||
case CODE_TRUE: printf("TRUE\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_LOAD_LOCAL: BYTE_INSTRUCTION("LOAD_LOCAL");
|
||||||
case CODE_STORE_LOCAL: BYTE_INSTRUCTION("STORE_LOCAL");
|
case CODE_STORE_LOCAL: BYTE_INSTRUCTION("STORE_LOCAL");
|
||||||
case CODE_LOAD_UPVALUE: BYTE_INSTRUCTION("LOAD_UPVALUE");
|
case CODE_LOAD_UPVALUE: BYTE_INSTRUCTION("LOAD_UPVALUE");
|
||||||
|
|||||||
@ -418,6 +418,15 @@ static bool runInterpreter(WrenVM* vm)
|
|||||||
&&code_NULL,
|
&&code_NULL,
|
||||||
&&code_FALSE,
|
&&code_FALSE,
|
||||||
&&code_TRUE,
|
&&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_LOAD_LOCAL,
|
||||||
&&code_STORE_LOCAL,
|
&&code_STORE_LOCAL,
|
||||||
&&code_LOAD_UPVALUE,
|
&&code_LOAD_UPVALUE,
|
||||||
@ -482,18 +491,20 @@ static bool runInterpreter(WrenVM* vm)
|
|||||||
#define INTERPRET_LOOP DISPATCH();
|
#define INTERPRET_LOOP DISPATCH();
|
||||||
#define CASE_CODE(name) code_##name
|
#define CASE_CODE(name) code_##name
|
||||||
|
|
||||||
#if WREN_DEBUG_TRACE_INSTRUCTIONS
|
#if WREN_DEBUG_TRACE_INSTRUCTIONS
|
||||||
// Prints the stack and instruction before each instruction is executed.
|
// Prints the stack and instruction before each instruction is executed.
|
||||||
#define DISPATCH() \
|
#define DISPATCH() \
|
||||||
{ \
|
{ \
|
||||||
wrenDebugPrintStack(fiber); \
|
wrenDebugPrintStack(fiber); \
|
||||||
wrenDebugPrintInstruction(vm, fn, (int)(ip - fn->bytecode)); \
|
wrenDebugPrintInstruction(vm, fn, (int)(ip - fn->bytecode)); \
|
||||||
instruction = *ip++; \
|
instruction = *ip++; \
|
||||||
goto *dispatchTable[instruction]; \
|
goto *dispatchTable[instruction]; \
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define DISPATCH() goto *dispatchTable[instruction = READ_BYTE()]
|
|
||||||
#endif
|
#define DISPATCH() goto *dispatchTable[instruction = READ_BYTE()];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@ -508,6 +519,33 @@ static bool runInterpreter(WrenVM* vm)
|
|||||||
Code instruction;
|
Code instruction;
|
||||||
INTERPRET_LOOP
|
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(POP): DROP(); DISPATCH();
|
||||||
CASE_CODE(NULL): PUSH(NULL_VAL); DISPATCH();
|
CASE_CODE(NULL): PUSH(NULL_VAL); DISPATCH();
|
||||||
CASE_CODE(FALSE): PUSH(FALSE_VAL); DISPATCH();
|
CASE_CODE(FALSE): PUSH(FALSE_VAL); DISPATCH();
|
||||||
@ -604,10 +642,6 @@ static bool runInterpreter(WrenVM* vm)
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
CASE_CODE(LOAD_LOCAL):
|
|
||||||
PUSH(fiber->stack[frame->stackStart + READ_BYTE()]);
|
|
||||||
DISPATCH();
|
|
||||||
|
|
||||||
CASE_CODE(STORE_LOCAL):
|
CASE_CODE(STORE_LOCAL):
|
||||||
fiber->stack[frame->stackStart + READ_BYTE()] = PEEK();
|
fiber->stack[frame->stackStart + READ_BYTE()] = PEEK();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
@ -730,17 +764,6 @@ static bool runInterpreter(WrenVM* vm)
|
|||||||
vm->globals.data[READ_SHORT()] = PEEK();
|
vm->globals.data[READ_SHORT()] = PEEK();
|
||||||
DISPATCH();
|
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):
|
CASE_CODE(STORE_FIELD_THIS):
|
||||||
{
|
{
|
||||||
int field = READ_BYTE();
|
int field = READ_BYTE();
|
||||||
|
|||||||
@ -40,6 +40,17 @@ typedef enum
|
|||||||
// Push true onto the stack.
|
// Push true onto the stack.
|
||||||
CODE_TRUE,
|
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].
|
// Pushes the value in local slot [arg].
|
||||||
CODE_LOAD_LOCAL,
|
CODE_LOAD_LOCAL,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user