JIT now use long jump instruction if needed, and pave way for type stack

This commit is contained in:
Gordon Williams 2022-11-09 16:36:36 +00:00
parent 9f3da7367b
commit 51ecf36d97
4 changed files with 67 additions and 22 deletions

View File

@ -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:

View File

@ -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

View File

@ -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) {

View File

@ -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)