From 0f21d49c867cf12a3beae40f20347ea290be9c67 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 17 Apr 2024 14:46:19 +0100 Subject: [PATCH] JIT: Fix issue where the 'jit' keyword got included in compiled code JIT: If using a builtin global function (eg digitalWrite), use it directly rather than searching by name --- ChangeLog | 2 ++ README_JIT.md | 10 +++++----- src/jsjit.c | 28 ++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6ccfab6a9e..3377fe02be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,8 @@ Storage.readJSON will now read numeric field names like "{1:1}" which can be produced by writeJSON (fix #2484) Ensure `Bangle.load(".bootcde")` just calls `load()` if no uiRemove - fix error loading clock without widgets Forward errors encountered while loading modules back into Espruino (fix #2485) + JIT: Fix issue where the 'jit' keyword got included in compiled code + JIT: If using a builtin global function (eg digitalWrite), use it directly rather than searching by name 2v21 : nRF52: free up 800b more flash by removing vector table padding Throw Exception when a Promise tries to resolve with another Promise (#2450) diff --git a/README_JIT.md b/README_JIT.md index 43b0743a39..b5d4472348 100644 --- a/README_JIT.md +++ b/README_JIT.md @@ -25,18 +25,18 @@ Works: Doesn't work: -* Everything else +* `new X(...)` +* Everything not mentioned under `Works` Performance: * When calling a JIT function, we use existing FunctionCall code to set up args and an execution scope (so args can be passed in) * Variables are referenced at the start just once and stored on the stack - * We could also maybe extend it to allow caching of constant field access, for instance 'console.log' -* Built-in functions could be called directly, which would be a TON faster + * We could also maybe extend it to allow caching of constant field accesses, for instance 'console.log' +* Built-in global functions are called directly which is a ton faster, but methods like 'console.log' are not currently * Peephole optimisation could still be added (eg. removing `push r0, pop r0`) but this is the least of our worries -* Stuff is in place to allow ints to be stored on the stack and converted when needed. This could maybe allow us to keep some vars as ints. +* Stuff is in place to allow ints to be stored on the stack and converted when needed. This could allow us to keep some vars as ints, but control flow makes this hard * When a function is called we load up the address as a 32 bit literal each time. We could maybe have a constant pool or local stub functions? -* When we emit code, we just use StringAppend which can be very slow. We should use an iterator (it's an easy win for compile performance) Possible improvements: diff --git a/src/jsjit.c b/src/jsjit.c index 64e605a66d..7833a60013 100644 --- a/src/jsjit.c +++ b/src/jsjit.c @@ -17,6 +17,7 @@ #include "jsjit.h" #include "jsjitc.h" #include "jsinteractive.h" +#include "jswrapper.h" #define JSP_ASSERT_MATCH(TOKEN) { assert(0+lex->tk==(TOKEN));jslGetNextToken(); } // Match where if we have the wrong token, it's an internal error #define JSP_MATCH_WITH_RETURN(TOKEN, RETURN_VAL) if (!jslMatch((TOKEN))) return RETURN_VAL; @@ -234,12 +235,31 @@ void jsjFactorIDAndUnLock(JsVar *name, LEX_TYPES creationOp) { // We don't have it yet - create a var list entry varIndexVal = jsvNewFromInteger(jit.varCount++); jsvSetValueOfName(varIndex, varIndexVal); - jsjcLiteralString(0, name, true); // null terminated string in r0 + // Now add the code which will create the variable right at the start of the file if (creationOp==LEX_ID) { // Just a normal ID - jsjcDebugPrintf("; Find Variable %j\n", name); - jsjcCall(jspGetNamedVariable); // Find the var in the current scopes (always returns something even if it's jsvNewChild) + // See if it's a builtin function, if builtinFunction!=0 + char tokenName[JSLEX_MAX_TOKEN_LENGTH]; + size_t tokenL = jsvGetString(name, tokenName, sizeof(tokenName)); + tokenName[tokenL] = 0; // null termination + JsVar *builtin = jswFindBuiltInFunction(0, tokenName); + if (jsvIsNativeFunction(builtin)) { // it's a built-in function - just create it in place rather than searching + jsjcDebugPrintf("; Native Function %j\n", name); + jsjcLiteral32(0, builtin->varData.native.ptr); + jsjcLiteral16(1, false, builtin->varData.native.argTypes); + jsjcCall(jsvNewNativeFunction); // JsVar *jsvNewNativeFunction(void (*ptr)(void), unsigned short argTypes) + } else if (jsvIsPin(builtin)) { // it's a built-in pin - just create it in place rather than searching + jsjcDebugPrintf("; Native Pin %j\n", name); + jsjcLiteral32(0, jsvGetInteger(builtin)); + jsjcCall(jsvNewFromPin); // JsVar *jsvNewNativeFunction(void (*ptr)(void), unsigned short argTypes) + } else { // it's not a builtin function - just search for the variable the normal way + jsjcDebugPrintf("; Find Variable %j\n", name); + jsjcLiteralString(0, name, true); // null terminated string in r0 + jsjcCall(jspGetNamedVariable); // Find the var in the current scopes (always returns something even if it's jsvNewChild) + } + jsvUnLock(builtin); } else if (creationOp==LEX_R_VAR || creationOp==LEX_R_LET || creationOp==LEX_R_CONST) { jsjcDebugPrintf("; Variable Decl %j\n", name); + jsjcLiteralString(0, name, true); // null terminated string in r0 // _jsxAddVar(r0:name) jsjcCall(_jsxAddVar); // add the variable } else assert(0); @@ -1110,7 +1130,7 @@ JsVar *jsjParseFunction() { // Function init code jsjFunctionStart(); // Parse the function - size_t codeStartPosition = lex->tokenLastStart; + size_t codeStartPosition = lex->tokenStart; // otherwise we include 'jit' too! jit.phase = JSJP_SCAN; DEBUG_JIT("; ============ SCAN PHASE\n"); jsjBlockNoBrackets(); if (JSJ_PARSING) { // if no error, re-parse and create code