From 51ecf36d975ee2740b59e56bf20e3f17556d8f4e Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 9 Nov 2022 16:36:36 +0000 Subject: [PATCH] JIT now use long jump instruction if needed, and pave way for type stack --- README_JIT.md | 16 ++++++++++++++++ src/jsjit.c | 35 ++++++++++++++++++++--------------- src/jsjitc.c | 33 +++++++++++++++++++++++++++------ src/jsjitc.h | 5 ++++- 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/README_JIT.md b/README_JIT.md index 0bc59f665..1fa8d4f01 100644 --- a/README_JIT.md +++ b/README_JIT.md @@ -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: diff --git a/src/jsjit.c b/src/jsjit.c index e88a81b35..32930777c 100644 --- a/src/jsjit.c +++ b/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 diff --git a/src/jsjitc.c b/src/jsjitc.c index 8e882e1cd..dd74a9b21 100644 --- a/src/jsjitc.c +++ b/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 + 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.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<=0 && reg<8); jsjcEmit16((uint16_t)(0b1011110000000000 | (1<