mirror of
https://github.com/espruino/Espruino.git
synced 2026-02-01 15:55:37 +00:00
JIT now use long jump instruction if needed, and pave way for type stack
This commit is contained in:
parent
9f3da7367b
commit
51ecf36d97
@ -171,6 +171,22 @@ jit(); // prints 'hello {b:42,...}'
|
||||
|
||||
function jit(a,b) {'jit';return a+"Hello world"+b;}
|
||||
jit(1,2)=="1Hello world2"
|
||||
|
||||
|
||||
function nojit() {
|
||||
for (var i=0;i<10000;i++) {
|
||||
digitalWrite(LED,1);
|
||||
digitalWrite(LED,0);
|
||||
}
|
||||
}
|
||||
function jit() {"jit";
|
||||
for (var i=0;i<10000;i++) {
|
||||
digitalWrite(LED,1);
|
||||
digitalWrite(LED,0);
|
||||
}
|
||||
}
|
||||
t=getTime();nojit();getTime()-t // 6.96
|
||||
t=getTime();jit();getTime()-t // 2.02
|
||||
```
|
||||
|
||||
Run JIT on ARM and then disassemble:
|
||||
|
||||
35
src/jsjit.c
35
src/jsjit.c
@ -115,7 +115,7 @@ NO_INLINE void _jsxVarInitialAssign(JsVar *a, bool isConstant, JsVar *initialVal
|
||||
|
||||
void jsjPopAsVar(int reg) {
|
||||
JsjValueType varType = jsjcPop(reg);
|
||||
if (varType==JSJVT_JSVAR) return;
|
||||
if (varType==JSJVT_JSVAR || varType==JSJVT_JSVAR_NO_NAME) return;
|
||||
if (varType==JSJVT_INT) {
|
||||
if (reg) jsjcMov(0, reg);
|
||||
jsjcCall(jsvNewFromInteger); // FIXME: what about clobbering r1-r3?
|
||||
@ -139,8 +139,13 @@ void jsjPopAndUnLock() {
|
||||
}
|
||||
|
||||
void jsjPopNoName(int reg) {
|
||||
if (jsjcGetTopType()==JSJVT_JSVAR_NO_NAME) {
|
||||
// if we know we don't have a name here, we can skip jsvSkipNameAndUnLock
|
||||
jsjPopAsVar(reg);
|
||||
return;
|
||||
}
|
||||
jsjPopAsVar(0); // a -> r0
|
||||
jsjcCall(jsvSkipNameAndUnLock); // optimisation: we should know if we have a var or a name here, so can skip jsvSkipNameAndUnLock sometimes
|
||||
jsjcCall(jsvSkipNameAndUnLock);
|
||||
if (reg != 0) jsjcMov(reg, 0);
|
||||
}
|
||||
|
||||
@ -229,7 +234,7 @@ void jsjFactor() {
|
||||
if (jit.phase == JSJP_EMIT) {
|
||||
jsjcLiteral64(0, *((uint64_t*)&v));
|
||||
jsjcCall(jsvNewFromFloat);
|
||||
jsjcPush(0, JSJVT_JSVAR);
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // a value, not a NAME
|
||||
}
|
||||
} else if (lex->tk=='(') {
|
||||
JSP_ASSERT_MATCH('(');
|
||||
@ -241,7 +246,7 @@ void jsjFactor() {
|
||||
if (jit.phase == JSJP_EMIT) {
|
||||
jsjcLiteral32(0, lex->tk==LEX_R_TRUE);
|
||||
jsjcCall(jsvNewFromBool);
|
||||
jsjcPush(0, JSJVT_JSVAR);
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // a value, not a NAME
|
||||
}
|
||||
JSP_ASSERT_MATCH(lex->tk);
|
||||
} else if (lex->tk==LEX_R_NULL) {
|
||||
@ -249,13 +254,13 @@ void jsjFactor() {
|
||||
if (jit.phase == JSJP_EMIT) {
|
||||
jsjcLiteral32(0, JSV_NULL);
|
||||
jsjcCall(jsvNewWithFlags);
|
||||
jsjcPush(0, JSJVT_JSVAR);
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // a value, not a NAME
|
||||
}
|
||||
} else if (lex->tk==LEX_R_UNDEFINED) {
|
||||
JSP_ASSERT_MATCH(LEX_R_UNDEFINED);
|
||||
if (jit.phase == JSJP_EMIT) {
|
||||
jsjcLiteral32(0, 0);
|
||||
jsjcPush(0, JSJVT_JSVAR);
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // a value, not a NAME
|
||||
}
|
||||
} else if (lex->tk==LEX_STR) {
|
||||
JsVar *a = jslGetTokenValueAsVar();
|
||||
@ -264,7 +269,7 @@ void jsjFactor() {
|
||||
int len = jsjcLiteralString(1, a, false);
|
||||
jsjcLiteral32(0, len);
|
||||
jsjcCall(jsvNewStringOfLength);
|
||||
jsjcPush(0, JSJVT_JSVAR);
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // a value, not a NAME
|
||||
}
|
||||
jsvUnLock(a);
|
||||
}/* else if (lex->tk=='{') {
|
||||
@ -292,7 +297,7 @@ void jsjFactor() {
|
||||
jsjUnaryExpression();
|
||||
jsjcCall(jsvUnLock);
|
||||
jsjcLiteral32(0, 0);
|
||||
jsjcPush(0, JSJVT_JSVAR);
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // a value, not a NAME
|
||||
}
|
||||
} else JSP_MATCH(LEX_EOF);
|
||||
}
|
||||
@ -375,7 +380,7 @@ void jsjFactorFunctionCall() {
|
||||
while (JSJ_PARSING && lex->tk!=')' && lex->tk!=LEX_EOF) {
|
||||
argCount++;
|
||||
jsjAssignmentExpression();
|
||||
if (jit.phase == JSJP_EMIT) {
|
||||
if (jit.phase == JSJP_EMIT) { // FIXME: why do we have this? it does nothing except convert to a var?
|
||||
jsjPopNoName(0);
|
||||
jsjcPush(0, JSJVT_JSVAR); // push argument to stack
|
||||
}
|
||||
@ -408,7 +413,7 @@ void jsjFactorFunctionCall() {
|
||||
jsjcCall(_jsjxFunctionCallAndUnLock); // a = _jsjxFunctionCallAndUnLock(funcName, thisArg/parent, isParsing, argCount, argPtr[on stack]);
|
||||
DEBUG_JIT("; FUNCTION CALL cleanup stack\n");
|
||||
jsjcAddSP(4*(1+argCount)); // pop off argPtr + all the arguments
|
||||
jsjcPush(0, JSJVT_JSVAR); // push return value from jspeFunctionCall
|
||||
jsjcPush(0, JSJVT_JSVAR); // push return value from jspeFunctionCall (FIXME: can we be sure this isn't a NAME so use JSJVT_JSVAR_NO_NAME? I think so)
|
||||
DEBUG_JIT("; FUNCTION CALL end\n");
|
||||
// 'parent', 'funcName' and all args are unlocked by _jsjxFunctionCallAndUnLock
|
||||
}
|
||||
@ -427,7 +432,7 @@ void __jsjPostfixExpression() {
|
||||
jsjPopAsVar(0); // old value -> r0
|
||||
jsjcLiteral32(1, op==LEX_PLUSPLUS ? '+' : '-'); // add the operation
|
||||
jsjcCall(_jsxPostfixIncDec); // JsVar *_jsxPostfixIncDec(JsVar *var, char op)
|
||||
jsjcPush(0, JSJVT_JSVAR); // push result (value BEFORE we inc/dec)
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // push result (value BEFORE we inc/dec)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -442,7 +447,7 @@ void jsjPostfixExpression() {
|
||||
jsjPopAsVar(0); // old value -> r0
|
||||
jsjcLiteral32(1, op==LEX_PLUSPLUS ? '+' : '-'); // add the operation
|
||||
jsjcCall(_jsxPrefixIncDec); // JsVar *_jsxPrefixIncDec(JsVar *var, char op)
|
||||
jsjcPush(0, JSJVT_JSVAR); // push result (value AFTER we inc/dec)
|
||||
jsjcPush(0, JSJVT_JSVAR); // push result (value AFTER we inc/dec) - this is STILL a NAME
|
||||
}
|
||||
} else
|
||||
jsjFactorFunctionCall();
|
||||
@ -471,7 +476,7 @@ void jsjUnaryExpression() {
|
||||
} else if (op=='+') { // unary plus (convert to number)
|
||||
jsjcCall(jsvAsNumberAndUnLock);
|
||||
} else assert(0);
|
||||
jsjcPush(0, JSJVT_JSVAR);
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // a value, not a NAME
|
||||
}
|
||||
} else
|
||||
jsjPostfixExpression();
|
||||
@ -575,7 +580,7 @@ void __jsjBinaryExpression(unsigned int lastPrecedence) {
|
||||
jsjPopAsVar(0); // a -> r0
|
||||
jsjcLiteral32(2, op);
|
||||
jsjcCall(_jsxMathsOpSkipNamesAndUnLock); // unlocks arguments
|
||||
jsjcPush(0, JSJVT_JSVAR); // push result
|
||||
jsjcPush(0, JSJVT_JSVAR_NO_NAME); // push result - a value, not a NAME
|
||||
}
|
||||
}
|
||||
precedence = jsjGetBinaryExpressionPrecedence(lex->tk);
|
||||
@ -608,7 +613,7 @@ NO_INLINE void jsjAssignmentExpression() {
|
||||
if (jit.phase == JSJP_EMIT) {
|
||||
jsjPopNoName(1); // ensure we get rid of any references on the RHS
|
||||
jsjcPop(0); // pop LHS
|
||||
jsjcPush(0, JSJVT_JSVAR); // push LHS back on as this is our result value
|
||||
jsjcPush(0, JSJVT_JSVAR); // push LHS back on as this is our result value (still a NAME)
|
||||
//jsjcPush(1, JSJVT_JSVAR); // push RHS back on, so we can pop it off and unlock after jsvReplaceWithOrAddToRoot
|
||||
|
||||
|
||||
|
||||
33
src/jsjitc.c
33
src/jsjitc.c
@ -227,19 +227,34 @@ void jsjcBranchRelative(int bytes) {
|
||||
DEBUG_JIT("B %s%d (addr 0x%04x)\n", (bytes>0)?"+":"", (uint32_t)(bytes), jsjcGetByteCount()+bytes);
|
||||
bytes -= 2; // because PC is ahead by 2
|
||||
assert(!(bytes&1)); // only multiples of 2 bytes
|
||||
assert(bytes>=-4096 && bytes<4096); // only multiples of 2 bytes
|
||||
assert(bytes>=-2048 && bytes<2048); // check it's in range...
|
||||
if (bytes<-2048 || bytes>=2048)
|
||||
jsExceptionHere(JSET_ERROR, "JIT: B jump (%d) out of range", bytes);
|
||||
int imm11 = ((unsigned int)(bytes)>>1) & 2047;
|
||||
jsjcEmit16((uint16_t)(0b1110000000000000 | imm11)); // unconditional branch
|
||||
}
|
||||
|
||||
// Jump a number of bytes forward or back, based on condition flags
|
||||
void jsjcBranchConditionalRelative(JsjAsmCondition cond, int bytes) {
|
||||
DEBUG_JIT("B[%d] %s%d (addr 0x%04x)\n", cond, (bytes>0)?"+":"", (uint32_t)(bytes), jsjcGetByteCount()+bytes);
|
||||
bytes -= 2; // because PC is ahead by 2
|
||||
assert(!(bytes&1)); // only multiples of 2 bytes
|
||||
assert(bytes>=-512 && bytes<512); // only multiples of 2 bytes
|
||||
int imm8 = (bytes>>1) & 255;
|
||||
jsjcEmit16((uint16_t)(0b1101000000000000 | (cond<<8) | imm8)); // conditional branch
|
||||
if (bytes>=-256 && bytes<256) { // B<c>
|
||||
DEBUG_JIT("B<%d> %s%d (addr 0x%04x)\n", cond, (bytes>0)?"+":"", (uint32_t)(bytes), jsjcGetByteCount()+bytes);
|
||||
int imm8 = (bytes>>1) & 255;
|
||||
jsjcEmit16((uint16_t)(0b1101000000000000 | (cond<<8) | imm8)); // conditional branch
|
||||
} else if (bytes>=-1048576 && bytes<(1048576-2)) { // B<c>.W
|
||||
bytes += 2; // must pad out by 1 byte because this is a double-length instruction!
|
||||
DEBUG_JIT("B<%d>.W %s%d (addr 0x%04x)\n", cond, (bytes>0)?"+":"", (uint32_t)(bytes), jsjcGetByteCount()+bytes);
|
||||
int imm20 = (bytes>>1);
|
||||
int S = (imm20>>19) & 1;
|
||||
int J2 = (imm20>>18) & 1;
|
||||
int J1 = (imm20>>17) & 1;
|
||||
int imm6 = (imm20>>11) & 63;
|
||||
int imm11 = imm20 & 2047;
|
||||
jsjcEmit16((uint16_t)(0b1111000000000000 | (S<<10) | (cond<<6) | imm6)); // conditional branch
|
||||
jsjcEmit16((uint16_t)(0b1000000000000000 | (J1<<13) | (J2<<11) | imm11)); // conditional branch
|
||||
} else
|
||||
jsExceptionHere(JSET_ERROR, "JIT: B<> jump (%d) out of range", bytes);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_JIT_CALLS
|
||||
@ -294,12 +309,18 @@ void jsjcPush(int reg, JsjValueType type) {
|
||||
jsjcEmit16((uint16_t)(0b1011010000000000 | (1<<reg)));
|
||||
}
|
||||
|
||||
// Get the type of the variable on the top of the stack
|
||||
JsjValueType jsjcGetTopType() {
|
||||
return JSJVT_JSVAR; // FIXME - no type stack yet!
|
||||
}
|
||||
|
||||
JsjValueType jsjcPop(int reg) {
|
||||
JsjValueType varType = jsjcGetTopType();
|
||||
jit.stackDepth--;
|
||||
DEBUG_JIT("POP {r%d} (<= stack depth %d)\n", reg, jit.stackDepth);
|
||||
assert(reg>=0 && reg<8);
|
||||
jsjcEmit16((uint16_t)(0b1011110000000000 | (1<<reg)));
|
||||
return JSJVT_JSVAR; // FIXME
|
||||
return varType;
|
||||
}
|
||||
|
||||
void jsjcAddSP(int amt) {
|
||||
|
||||
@ -27,7 +27,8 @@ void jsjcDebugPrintf(const char *fmt, ...);
|
||||
|
||||
typedef enum {
|
||||
JSJVT_INT,
|
||||
JSJVT_JSVAR
|
||||
JSJVT_JSVAR, ///< A JsVar
|
||||
JSJVT_JSVAR_NO_NAME ///< A JsVar, and we know it's not a name so it doesn't need SkipName
|
||||
} JsjValueType;
|
||||
|
||||
typedef enum {
|
||||
@ -130,6 +131,8 @@ void jsjcMVN(int regTo, int regFrom);
|
||||
void jsjcAND(int regTo, int regFrom);
|
||||
// Push a register onto the stack
|
||||
void jsjcPush(int reg, JsjValueType type);
|
||||
// Get the type of the variable on the top of the stack
|
||||
JsjValueType jsjcGetTopType();
|
||||
// Pop off the stack to a register
|
||||
JsjValueType jsjcPop(int reg);
|
||||
// Add a value to the stack pointer (only multiple of 4)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user