Skip to content

Commit

Permalink
JIT: Fix issue where the 'jit' keyword got included in compiled code
Browse files Browse the repository at this point in the history
JIT: If using a builtin global function (eg digitalWrite), use it directly rather than searching by name
  • Loading branch information
gfwilliams committed Apr 17, 2024
1 parent 176a23c commit 0f21d49
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 9 deletions.
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 5 additions & 5 deletions README_JIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
28 changes: 24 additions & 4 deletions src/jsjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 0f21d49

Please sign in to comment.