gfwilliams / espruinocompiler Goto Github PK
View Code? Open in Web Editor NEWNode.js-based JavaScript->C++ compiler
Node.js-based JavaScript->C++ compiler
see this post (near the end) for a sample which could be used for testing (tested on an original Espruino, no need to actually attach WS2812 LEDs)
Currently, the mentioned code contains three major functions, one of them still runs when "compiled", adding "compiled" to the other ones produces no compiler errors but - when uploaded into flash (because of its length) and started - locks the whole device (I had to reflash the firmware in order to get the board operational again)
This is a K.O. bug as it turns out to be so unpredictable (you never know whether compiled code will run or not) and so labour-intensive (because the firmware has to be flashed again)
Originally: espruino/EspruinoTools#8
Currently, if I write:
function f() {
"compiled";
a = 1;
a = 2;
a = 3;
}
It does 3 symbol table lookups for a
. Ideally it'd look a
up once and then keep it for future use.
Even more of an issue for something like:
function f() {
"compiled";
digitalWrite(LED1,0);
digitalWrite(LED1,1);
digitalWrite(LED1,0);
digitalWrite(LED1,1);
}
Where both digitalWrite and LED1 get looked up each time.
While potentially that causes issues with something like this:
function setA() {
a=1;
}
function f() {
"compiled";
console.log(a);
setA();
console.log(a);
}
The first use of a
should have failed with a ReferenceError (but it doesn't in Espruino) so we're probably safe to ignore it.
When I sent code optimized the function with "compiled", It prompt "Compiler not active as no process.env.EXPORTS available". How to fix it ?
The firmware version is 1v17.24, and The "EXPORTS" variable has no values.
Currently if I write:
function a(x) {
"compiled";
print(x|0);
}
You get something a bit like like:
void a(JsVar *x) {
print(jsvGetInteger(x)|0);
}
We know that x|0
is an integer already, so ideally:
x|0
then detected as a no-op and removed (although the compiler would do this so do we care?)void a(int x) {
print(x);
}
Something like this:
function checksum(m) {
"compiled";
var ck = 0;
var l = 0|m.length;
var c = m.charCodeAt.bind(m);
for (var i=0;i<l;i++) ck ^= c(i);
return ck;
}
Isn't as fast as it could be, because ck
is treated as a JsVar, not an int.
for (j=0, k = RowStart; j < 16; j++,k++)
can be of course rewritten but maybe it is not that hard to add?
Originally: espruino/EspruinoTools#9
And also: espruino/Espruino#332
It'd be nice to make sure we compile asm.js code relatively efficiently. I think the main requirements are:
Currently a simple FOR loop can fall off the fast path:
var l = "Hello".length;
for (var i=0;i<l;i++) ;
Will generate code that does 'l' (and hence the comparison) with JsVars instead of ints
see also conversation https://forum.espruino.com/conversations/383232/
I also remember hitting this. Workaround is to initialize variable with nonzero value like int a = -1;
below so that it goes into .data section, not .bss
var c = E.compiledC(`
// void init()
// int f()
int a = -1;
void init() {
a = 0;
}
int f() {
a++;
return a;
}
`);
Here is the difference
not initialized or initialized to zero ends with:
00000018 <_Z4initv>:
18: 4b02 ldr r3, [pc, #8] ; (24 <_Z4initv+0xc>)
1a: 447b add r3, pc
1c: 2200 movs r2, #0
1e: 601a str r2, [r3, #0]
20: 4770 bx lr
22: bf00 nop
24: 0000000a andeq r0, r0, sl
Disassembly of section .bss:
00000028 <a>:
28: 00000000 andeq r0, r0, r0
----------------------------------------
(function(){
var bin=atob("Akt7RBhoATAYYHBHGgAAAAJLe0QAIhpgcEcAvwoAAAA=");
return {
init:E.nativeCall(17, "void()", bin),
f:E.nativeCall(1, "int()", bin),
};
})()
----------------------------------------
Initialized ends with
00000018 <_Z4initv>:
18: 4b02 ldr r3, [pc, #8] ; (24 <_Z4initv+0xc>)
1a: 447b add r3, pc
1c: 2200 movs r2, #0
1e: 601a str r2, [r3, #0]
20: 4770 bx lr
22: bf00 nop
24: 0000000a andeq r0, r0, sl
Disassembly of section .data:
00000028 <a>:
28: ffffffff ; <UNDEFINED> instruction: 0xffffffff
----------------------------------------
(function(){
var bin=atob("Akt7RBhoATAYYHBHGgAAAAJLe0QAIhpgcEcAvwoAAAD/////");
return {
init:E.nativeCall(17, "void()", bin),
f:E.nativeCall(1, "int()", bin),
};
})()
----------------------------------------
it can be seen that the var bin
string is longer in initialized case woAAAA
vs woAAAD/////
One possible fix is to put everything into text segment in the linker file as we load to RAM anyway and don't support separate data vs code and relocation of data references. Or at least the .bss
can be included into .data
.
diff --git a/inc/linker.ld b/inc/linker.ld
index 7ea3647..7d4d609 100644
--- a/inc/linker.ld
+++ b/inc/linker.ld
@@ -22,17 +22,21 @@ SECTIONS
*(.rodata)
*(.rodata*)
. = ALIGN(4);
+/*
} >RAM
.data :
{
. = ALIGN(4);
+*/
*(.data) /* .data sections */
*(.data*) /* .data* sections */
+/*
} >RAM
.bss :
{
+*/
/* This is used by the startup in order to initialize the .bss secion */
*(.bss)
*(.bss*)
One example in https://www.espruino.com/InlineC is using double type however the caveats section mentions "Arithmetic with floats, doubles and uint64 may not work since it requires functions that won't be compiled in."
I tried the mandelbrot javascript example as inline C (just replacing var by int and double or float like
var c = E.compiledC(`
// int mand(int, int)
int mand(int x,int y){
double Xr = 0;
double Xi = 0;
int i = 0;
double Cr=(4*x/64)-2;
double Ci=(4*y/64)-2;
while ((i<32) & ((Xr*Xr+Xi*Xi)<4)) {
double t=Xr*Xr - Xi*Xi + Cr;
Xi=2*Xr*Xi+Ci;
Xr=t;
i=i+1;
}
return i;
}
`);
An indeed it doesn't work and compiler produces
>---------------------------------------------------
COMPILER MESSAGE
---------------------------------------------------
/tmp/ccpIbvpd.ltrans0.ltrans.o: In function `mand(int, int)':
<artificial>:(.text._Z4mandii+0x10): undefined reference to `__aeabi_i2d'
<artificial>:(.text._Z4mandii+0x1e): undefined reference to `__aeabi_i2d'
<artificial>:(.text._Z4mandii+0x3a): undefined reference to `__aeabi_dmul'
<artificial>:(.text._Z4mandii+0x4a): undefined reference to `__aeabi_dmul'
<artificial>:(.text._Z4mandii+0x68): undefined reference to `__aeabi_dadd'
<artificial>:(.text._Z4mandii+0x74): undefined reference to `__aeabi_dcmplt'
<artificial>:(.text._Z4mandii+0x8c): undefined reference to `__aeabi_dsub'
<artificial>:(.text._Z4mandii+0x94): undefined reference to `__aeabi_dadd'
<artificial>:(.text._Z4mandii+0xa4): undefined reference to `__aeabi_dadd'
<artificial>:(.text._Z4mandii+0xac): undefined reference to `__aeabi_dmul'
<artificial>:(.text._Z4mandii+0xb8): undefined reference to `__aeabi_dadd'
collect2: error: ld returned 1 exit status
---------------------------------------------------
I see there are quite lot of methods exported via process.env.EXPTR for the javascript compiler to work. Would it work if the export list was extended by those eabi floating point methods listed above? Or are there other fundamental issues why this was not done? All those are probably linked inside espruino binary already so it would just enlarge the export table a bit by couple of pointers?
So, I use some compiled javascript functions and get unpredictable results:
a- the compiler is not accessible
b- the complied code is rejected by the board and I get the non compiled javascript function
c- the compiled code seems to be non functionnal, this creates some random behaviour later.
d-the compiled code is apparently ok but the next javascript, uncompiled, function is ignored and not defined
e- the IDE complains at upload time that some parenthesis are not closed... Which is impossible because those compiled functions are unchanged from one upload to the other and the IDE doesn't show any warning at the beginnig of any line.
Of course it also happens to work correctly but this is about 1/4th of the uploads.
Commenting out "compiled" instructions does allow a correct execution of the code, just half the speed of compiled code.
The URL used for the compiler is "http://www.espruino.com:32766".
The IDE is v0.56.1 but this happened with v0.56.0 too
The example below is the most common one.
>reset();
=undefined
_____ _
| __|___ ___ ___ _ _|_|___ ___
| __|_ -| . | _| | | | | . |
|_____|___| _|_| |___|_|_|_|___|
|_| http://espruino.com
1v80.323 Copyright 2015 G.Williams
>echo(0);
Uncaught SyntaxError: Got ID:JsVar expected ','
at line 1 col 192
... ddmmyy = E.nativeCall(1, "JsVar(JsVar)", atob("LenwTwVGd0iP...
Or also with throtte send, for exactly the same source code: This is just the same result actually, the functions in trouble seems to be randomly "selected".
reset();
=undefined
_____ _
| __|___ ___ ___ _ _|_|___ ___
| __|_ -| . | _| | | | | . |
|_____|___| _|_| |___|_|_|_|___|
|_| http://espruino.com
1v80.323 Copyright 2015 G.Williams
>echo(0);
Uncaught SyntaxError: Got ID:JsVar expected ','
at line 1 col 534
...ar secn = E.nativeCall(1, "JsVar(JsVar)", atob("LenwT4NNjbAH...
Excerpt of the code in trouble
// ---- beg (modified) GPS module code
// convert latitudes and longitudes from nmea ascii coding to float
function lln(d1,d2,negs) {
//"compiled";
var dfp=d1.indexOf(".")-2;
var v=(parseInt(d1.substr(0,dfp),10)+parseFloat(d1.substr(dfp))/60);
if (d2==negs) {
return (0-v);
} else { return v; }
}
function secn(d) {
//"compiled";
return (parseInt(d.substr(0,2),10)*3600+parseInt(d.substr(2,2),10)*60+parseFloat(d.substr(4,5)));
} // hhmmss.ss
function ddmmyy(d) {
//"compiled";
return Date.parse('20'+d.substr(4,2)+"-"+d.substr(2,2)+'-'+d.substr(0,2));
}
function cks7(m) {
//"compiled";
var ck = 0;
var l = 0|m.length; // ensure we don't have to check the length each time
var c = m.charCodeAt.bind(m); // ensure we don't have to look up 'charCodeAt' each time
for (var i=0;i<l;i++) ck ^= c(i);
return ck;
}
function checksum(msg) {
var cks = 0;
new Uint8Array(E.toArrayBuffer(msg)).forEach(function (c) { cks^=c; });
return cks;
}
// compute the checksum for NMEA sentences and splits it from ','
function nmea_checksum(s) {
try {
var n=s.split('$').pop(); // remove leading uncomplete sentences AND the no more usefull leading $
n=n.split('*'); // separate checksum from line
var cksum = parseInt(n.pop(),16); // make checksum a number and remove it from n
n=n.pop(); // keep content alone
if (cksum===cks7(n)) // mean leap 0.02532994300 s
return n.split(','); // mean leap 0.01426320198 s
else {
throw "different checksums "+cksum.toString(16)+" versus "+cks7(n).toString(16)+" in line "+s ;
}
} catch(x) {
throw "Checksum error "+x+' in sentence '+s;
}
}
"compiled" JS code
for (; i < 16; i++)
gives
TypeError: Cannot read property 'type' of null
at handle (/home/fanoush/EspruinoCompiler/src/compile.js:277:12)
at Object.ForStatement (/home/fanoush/EspruinoCompiler/src/compile.js:259:22)
at handle (/home/fanoush/EspruinoCompiler/src/compile.js:278:35)
at /home/fanoush/EspruinoCompiler/src/compile.js:406:9
at Array.forEach (<anonymous>)
at Object.exports.jsToC (/home/fanoush/EspruinoCompiler/src/compile.js:404:18)
at exports.compileFunction (/home/fanoush/EspruinoCompiler/src/compile.js:533:26)
at compile_js (/home/fanoush/EspruinoCompiler/server.js:18:5)
at handlePost (/home/fanoush/EspruinoCompiler/server.js:59:7)
at IncomingMessage.<anonymous> (/home/fanoush/EspruinoCompiler/server.js:110:7)
===============================================
because of node
"init": null,
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.