Commit 7579f40d authored by Camil Staps's avatar Camil Staps 🚀

Merge branch 'move-supporting-javascript-to-abc-interpreter-repo' into 'master'

Move supporting javascript to abc interpreter repo

See merge request !110
parents 27cc66bf f6e8be64
Pipeline #26109 passed with stages
in 20 minutes and 19 seconds
abc-instructions.js
abc-interpreter.wat
WebPublic
JS?=js
WASM:=abc-interpreter.wasm abc-interpreter-util.wasm
TARGETS:=$(WASM) abc-instructions.js
all: $(TARGETS) WebPublic
all: $(WASM) WebPublic
WebPublic: $(TARGETS)
WebPublic: $(WASM) abc-interpreter.js ../src/abc_instructions.h
mkdir -p WebPublic/js
cp $^ WebPublic/js
cp $(WASM) WebPublic/js
cp abc-interpreter.js WebPublic/js
echo 'ABCInterpreter.instructions=[' >> WebPublic/js/abc-interpreter.js
sed -n 's/^\tINSTRUCTION(\(.*\))/\t"\1",/p' ../src/abc_instructions.h >> WebPublic/js/abc-interpreter.js
echo '];' >> WebPublic/js/abc-interpreter.js
%.wasm: %.wat
$(JS) ../tools/wat2wasm.js $< -o $@
......@@ -16,9 +19,4 @@ abc-interpreter.wat: .FORCE
$(MAKE) -C ../tools interpretergenwasm
../tools/interpretergenwasm $(INTERPRETERGENWASMFLAGS) -i ../src/abc_instructions.h > $@
abc-instructions.js: ../src/abc_instructions.h
echo 'var abc_instructions = [' > $@
sed -n 's/^\tINSTRUCTION(\(.*\))/\t"\1",/p' $< >> $@
echo '];' >> $@
.FORCE:
......@@ -151,6 +151,57 @@
)
)
(func (export "decode_prelinked_bytecode") (param $ptr i32)
(local $section-len i32)
(local $len i32)
(local $i i32)
(local $byte i64)
(local $val i64)
(local $shift i64)
(local $neg i32)
(local.set $len (local.get $ptr))
(local.set $i (i32.const 0))
(loop $sections
(local.set $section-len (i32.load (local.get $ptr)))
(if (i32.eqz (local.get $section-len)) (return))
(local.set $ptr (i32.add (local.get $ptr) (i32.const 4)))
(loop $decode
(local.set $byte (i64.load8_u (local.get $ptr)))
(local.set $val (i64.and (local.get $byte) (i64.const 0x3f)))
(local.set $neg (i32.wrap_i64 (i64.and (local.get $byte) (i64.const 0x40))))
(local.set $shift (i64.const -1))
(block $end-value
(loop $lp
(local.set $ptr (i32.add (local.get $ptr) (i32.const 1)))
(br_if $end-value (i32.eqz (i32.wrap_i64 (i64.and (local.get $byte) (i64.const 0x80)))))
(local.set $shift (i64.add (local.get $shift) (i64.const 7)))
(local.set $byte (i64.load8_u (local.get $ptr)))
(local.set $val (i64.or (local.get $val)
(i64.shl (i64.and (local.get $byte) (i64.const 0x7f)) (local.get $shift))))
(br $lp)
)
)
(if
(local.get $neg)
(then (local.set $val (i64.sub (i64.const 0) (local.get $val))))
)
(i64.store (local.get $i) (local.get $val))
(local.set $i (i32.add (local.get $i) (i32.const 8)))
(br_if $decode (local.tee $section-len (i32.sub (local.get $section-len) (i32.const 1))))
)
(local.set $ptr (i32.and (i32.add (local.get $ptr) (i32.const 7)) (i32.const 0xfffffff8)))
(br $sections)
)
)
(func (export "gc") (param $asp i32)
(local $old i32)
(local $new i32)
......
This diff is collapsed.
if (typeof scriptArgs == 'undefined')
scriptArgs = arguments;
var progbytes = read(scriptArgs[scriptArgs.length-1], 'binary');
var prog = 'buffer' in progbytes
? new Uint32Array(progbytes.buffer) // spidermonkey
: new Uint32Array(progbytes); // d8
function fetch(path) {
if (path.indexOf('/js/')==0) {
return Promise.resolve({
ok: true,
arrayBuffer: () => os.file.readRelativeToScript(path.slice(4), 'binary').buffer,
});
}
var prog=read(path, 'binary');
if ('buffer' in prog) // spidermonkey
prog=prog.buffer;
return Promise.resolve({
ok: true,
arrayBuffer: () => prog,
});
}
var stack_size=(512<<10)*2;
loadRelativeToScript('abc-interpreter.js');
var stack_size=512<<10;
var heap_size=2<<20;
function string_to_size(s) {
......@@ -40,251 +55,105 @@ function string_to_size(s) {
return -1;
}
var heapi=scriptArgs.lastIndexOf('-h');
const heapi=scriptArgs.lastIndexOf('-h');
if (heapi>=0)
heap_size=string_to_size(scriptArgs[heapi+1]);
heap_size=Math.floor(heap_size/8)*8;
var stacki=scriptArgs.lastIndexOf('-s');
const stacki=scriptArgs.lastIndexOf('-s');
if (stacki>=0)
stack_size=string_to_size(scriptArgs[stacki+1]);
stack_size*=8;
var asp=4*prog.length;
var bsp=asp+stack_size;
var csp=asp+stack_size/2;
var hp=bsp+8;
var blocks_needed = Math.floor((prog.length*4 + stack_size + heap_size*2 + 65535) / 65536);
var memory = new WebAssembly.Memory({initial: blocks_needed});
var membuffer = new Uint32Array(memory.buffer);
var start=undefined;
var code_offset=undefined;
function find_offsets(prog) {
var i=0;
while (prog.length > 0) {
if (prog[0]==1) /* ST_Code */
code_offset=i+1;
if (prog[0]==3) /* ST_Start */
start=prog[2];
i+=1+prog[1];
prog=prog.slice(2+2*prog[1]);
}
}
find_offsets(prog);
if (start == undefined) {
crash('program has no start address');
}
for (var i in prog) {
membuffer[i] = prog[i];
}
loadRelativeToScript('abc-instructions.js');
var util = os.file.readRelativeToScript('abc-interpreter-util.wasm', 'binary');
util = new Uint8Array(util);
var intp = os.file.readRelativeToScript('abc-interpreter.wasm', 'binary');
intp = new Uint8Array(intp);
(async function(util, intp){
util = await WebAssembly.instantiate(util,
{
clean: {
memory: memory,
has_host_reference: function(index) {
return 0;
},
update_host_reference: function(index,new_location) {
},
set_hp: hp => intp.instance.exports.set_hp(hp),
set_hp_free: free => intp.instance.exports.set_hp_free(free),
gc_start: function() {
},
js_ref_found: function(val) {
},
gc_end: function() {
},
debug: function(what,a,b,c) {
switch (what) {
case 0:
console.log('loop',a,'/',b,'; hp at',c);
break;
case 1:
console.log('desc',a);
break;
case 2:
console.log('hnf, arity',a);
break;
case 3:
console.log('thunk, arity',a);
break;
}
stack_size*=2;
ABC=null;
ABCInterpreter.instantiate({
bytecode_path: scriptArgs[scriptArgs.length-1],
heap_size: heap_size,
stack_size: stack_size,
interpreter_imports: {
debug_instr: function (addr, instr) {
printErr((addr/8-ABC.code_offset)+'\t'+ABCInterpreter.instructions[instr]);
},
illegal_instr: function (addr, instr) {
crash('illegal instruction '+instr+' ('+ABCInterpreter.instructions[instr]+') at address '+(addr/8-ABC.code_offset-ABC.code_offset-ABC.code_offset-ABC.code_offset-ABC.code_offset-ABC.code_offset-ABC.code_offset-ABC.code_offset-ABC.code_offset));
},
out_of_memory: function () {
crash('out of memory');
},
halt: function (pc, hp_free, hp_size) {
print('halt at', (pc/8)-ABC.code_offset);
print(hp_size-hp_free, hp_free, hp_size);
},
putchar: function (v) {
putstr(String.fromCharCode(v));
},
print_int: function (high,low) {
if (high==0 && low>=0) {
putstr(String(low));
} else {
var n=BigInt(high)*2n**32n;
if (low<0) {
n+=2n**31n;
low+=2**31;
}
n+=BigInt(low);
putstr(String(n));
}
}
);
util.instance.exports.setup_gc(hp, heap_size, asp, 97*8);
intp = await WebAssembly.instantiate(intp,
{
clean: {
memory: memory,
debug_instr: function (addr, instr) {
printErr((addr/8-code_offset)+'\t'+abc_instructions[instr]);
},
handle_illegal_instr: function (pc, instr, asp, bsp, csp, hp) {
return 0;
},
illegal_instr: function (addr, instr) {
crash('illegal instruction '+instr+' ('+abc_instructions[instr]+') at address '+(addr/8-1));
},
out_of_memory: function () {
crash('out of memory');
},
gc: util.instance.exports.gc,
halt: function (pc, hp_free, hp_size) {
print('halt at', (pc/8)-code_offset);
print(hp_size-hp_free, hp_free, hp_size);
},
memcpy: util.instance.exports.memcpy,
strncmp: util.instance.exports.strncmp,
putchar: function (v) {
putstr(String.fromCharCode(v));
},
print_int: function (high,low) {
if (high==0 && low>=0) {
putstr(String(low));
} else {
var n=BigInt(high)*2n**32n;
if (low<0) {
n+=2n**31n;
low+=2**31;
}
n+=BigInt(low);
putstr(String(n));
}
},
print_bool: function (v) {
putstr(v==0 ? 'False' : 'True');
},
print_char: function (v) {
putstr("'"+String.fromCharCode(v)+"'");
},
print_real: function (v) {
putstr(Number(0+v).toLocaleString(
['en-US'],
{
useGrouping: false,
maximumSignificantDigits: 15,
}
));
},
powR: Math.pow,
acosR: Math.acos,
asinR: Math.asin,
atanR: Math.atan,
cosR: Math.cos,
sinR: Math.sin,
tanR: Math.tan,
expR: Math.exp,
lnR: Math.log,
log10R: Math.log10,
RtoAC_words_needed: function (v) {
v=Number(0+v).toLocaleString(
['en-US'],
{
useGrouping: false,
maximumSignificantDigits: 15,
}
);
return 2+((v.length+7)>>3);
},
RtoAC: function (dest, v) {
v=Number(0+v).toLocaleString(
['en-US'],
{
useGrouping: false,
maximumSignificantDigits: 15,
}
);
membuffer[dest/4]=6*8+2; // __STRING__
membuffer[dest/4+1]=0;
membuffer[dest/4+2]=v.length;
membuffer[dest/4+3]=0;
var arr=new Uint8Array(membuffer.buffer, dest+16);
for (var i=0; i<v.length; i++)
arr[i]=v.charCodeAt(i);
return dest+16+(((v.length+7)>>3)<<3);
},
}
}
);
if (scriptArgs.indexOf('--extract-code') >= 0) {
var obj = wasmExtractCode(intp.module, 'best');
},
print_bool: function (v) {
putstr(v==0 ? 'False' : 'True');
},
print_char: function (v) {
putstr("'"+String.fromCharCode(v)+"'");
},
print_real: function (v) {
putstr(Number(0+v).toLocaleString(
['en-US'],
{
useGrouping: false,
maximumSignificantDigits: 15,
}
));
},
}
}).then(function(abc){
ABC=abc;
const extractable = ['interpret'];
if (scriptArgs.indexOf('--extract-code')>=0) {
const obj=wasmExtractCode(abc.interpreter.module, 'best');
var exports = {};
for (var f in intp.instance.exports)
exports[intp.instance.exports[f].name] = f;
const extractable=['interpret'];
obj.segments.filter(function (seg) {
return seg.kind == 0 &&
extractable.indexOf(exports[seg.funcIndex]) >= 0;
}).map(function (seg) {
var name = seg.funcIndex in exports ? exports[seg.funcIndex] : ('_'+seg.funcIndex);
var filename = 'disas-'+name+'.bin';
var code = obj.code.subarray(seg.funcBodyBegin, seg.funcBodyEnd);
var exports={};
for (var f in abc.interpreter.instance.exports)
exports[abc.interpreter.instance.exports[f].name]=f;
printErr('extracting '+filename+'...');
os.file.writeTypedArrayToFile(filename, code);
});
}
obj.segments
.filter(seg => seg.kind==0 && extractable.indexOf(exports[seg.funcIndex])>=0)
.map(function (seg) {
const name=seg.funcIndex in exports ? exports[seg.funcIndex] : ('_'+seg.funcIndex);
const filename='disas-'+name+'.bin';
const code=obj.code.subarray(seg.funcBodyBegin, seg.funcBodyEnd);
var i=scriptArgs.indexOf('--graph');
if (i >= 0) {
var graph = os.file.readFile(scriptArgs[i+1], 'binary');
graph = new Uint32Array(graph.buffer);
var unused_semispace = util.instance.exports.get_unused_semispace();
for (var i=0; i<graph.length; i++)
membuffer[unused_semispace/4+i] = graph[i];
var node = hp;
hp = util.instance.exports.copy_from_string(unused_semispace,graph.length/2,asp,bsp,hp,code_offset*8);
asp+=8;
membuffer[asp/4] = node;
start+=32; /* skip bootstrap to build start node; jump to _print_graph */
printErr('extracting '+filename+'...');
os.file.writeTypedArrayToFile(filename, code);
});
}
var time_start=new Date().getTime();
if (!abc.start)
crash('program has no start address');
abc.interpreter.instance.exports.set_pc(abc.start);
intp.instance.exports.set_pc(start);
intp.instance.exports.set_asp(asp);
intp.instance.exports.set_bsp(bsp);
intp.instance.exports.set_csp(csp);
intp.instance.exports.set_hp(hp);
intp.instance.exports.set_hp_free(heap_size/8);
intp.instance.exports.set_hp_size(heap_size/8);
const time_start=new Date().getTime();
var r=intp.instance.exports.interpret();
const r=abc.interpreter.instance.exports.interpret();
if (r!=0)
printErr('failed with return code', r);
if (scriptArgs.indexOf('--time') >= 0) {
var time_end=new Date().getTime();
var time=(time_end-time_start)/1000;
const time_end=new Date().getTime();
const time=(time_end-time_start)/1000;
printErr('user '+time.toLocaleString(['en-US'], {maximumFractionDigits: 2}));
}
})(util, intp);
});
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "abc_instructions.h"
......@@ -19,7 +20,8 @@ enum section_type {
#define _4chars2int(a,b,c,d) ((uint64_t) (a+(b<<8)+(c<<16)+(d<<24)))
#define _7chars2int(a,b,c,d,e,f,g) ((uint64_t) (a+(b<<8)+(c<<16)+(d<<24)+((uint64_t)e<<32)+((uint64_t)f<<40)+((uint64_t)g<<48)))
#define _8chars2int(a,b,c,d,e,f,g,h) ((uint64_t) (a+(b<<8)+(c<<16)+(d<<24)+((uint64_t)e<<32)+((uint64_t)f<<40)+((uint64_t)g<<48)+((uint64_t)h<<56)))
uint64_t prelinker_preamble[669] = {
uint64_t prelinker_preamble[670] = {
0, /* reserved */
/* 0 */ 0, 0, 0, 7, _7chars2int('_','A','R','R','A','Y','_'),
/* 5 */ 0, 0, 0, 8, _8chars2int('_','S','T','R','I','N','G','_'),
/* 10 */ 0, 0, 0, 4, _4chars2int('B','O','O','L'),
......@@ -70,18 +72,18 @@ uint64_t prelinker_preamble[669] = {
void prepare_preamble(void) {
for (int i=0; i<=32; i++) {
prelinker_preamble[30+i*2]=26*8+2; /* INT+2 */
prelinker_preamble[30+i*2+1]=i;
prelinker_preamble[31+i*2]=26*8+2; /* INT+2 */
prelinker_preamble[31+i*2+1]=i;
}
for (int i=0; i<256; i++) {
prelinker_preamble[146+i*2]=16*8+2; /* CHAR+2 */
prelinker_preamble[146+i*2+1]=i;
prelinker_preamble[147+i*2]=16*8+2; /* CHAR+2 */
prelinker_preamble[147+i*2+1]=i;
}
prelinker_preamble[667]=prelinker_preamble[665]=11*8+2; /* BOOL+2 */
prelinker_preamble[666]=0;
prelinker_preamble[668]=1;
prelinker_preamble[668]=prelinker_preamble[666]=11*8+2; /* BOOL+2 */
prelinker_preamble[667]=0;
prelinker_preamble[669]=1;
}
void write_section(FILE *f, enum section_type type, uint32_t len, uint64_t *data) {
......@@ -90,6 +92,47 @@ void write_section(FILE *f, enum section_type type, uint32_t len, uint64_t *data
fwrite(data, sizeof(uint64_t), len, f);
}
static uint32_t varwidth_encode(uint32_t len, uint64_t *data, uint64_t **dest) {
char *ptr=safe_malloc (len*sizeof(uint64_t)*5/4); /* rough upper bound */
*dest=(uint64_t*)ptr;
*(uint32_t*)ptr=len;
ptr+=4;
while (len--) {
uint64_t val=*data++;
char byte=0x00;
if ((int64_t)val<0) {
val=0-(int64_t)val;
byte=0x40;
}
byte|=val&0x3f;
val>>=6;
if (val)
byte|=0x80;
*ptr++=byte;
while (val) {
byte=val&0x7f;
val>>=7;
if (val)
byte|=0x80;
*ptr++=byte;
}
}
return ((ptr-(char*)*dest)+7)>>3;
}
static void write_section_varwidth(FILE *f, enum section_type type, uint32_t len, uint64_t *data) {
uint64_t *leb_encoded_data;
uint32_t leb_encoded_len=varwidth_encode (len,data,&leb_encoded_data);
write_section (f,type,leb_encoded_len,leb_encoded_data);
free (leb_encoded_data);
}
int main(int argc, char **argv) {
char *output_file_name=NULL;
FILE *input_file=NULL;
......@@ -137,9 +180,9 @@ int main(int argc, char **argv) {
struct program *program=state.program;
prepare_preamble();
write_section(output_file, ST_Preamble, sizeof(prelinker_preamble)/sizeof(uint64_t), prelinker_preamble);
write_section(output_file, ST_Code, program->code_size, program->code);
write_section(output_file, ST_Data, program->data_size, program->data);
write_section_varwidth(output_file, ST_Preamble, sizeof(prelinker_preamble)/sizeof(uint64_t), prelinker_preamble);
write_section_varwidth(output_file, ST_Code, program->code_size, program->code);
write_section_varwidth(output_file, ST_Data, program->data_size, program->data);
write_section(output_file, ST_Start, 1, &program->symbol_table[program->start_symbol_id].offset);
fclose(output_file);
......
#pragma once
extern uint64_t prelinker_preamble[669];
extern uint64_t prelinker_preamble[670];
......@@ -255,10 +255,8 @@ int parse_program(struct parser *state, struct char_provider *cp) {
# else
state->program->data_size = elem32;
# endif
/* The prelinker writes data size between code and data segment, so reserve this space.
* TODO: better would be to use a different file format in the prelinker. */
state->program->code = safe_malloc(sizeof(BC_WORD) * (code_size+state->program->data_size+1));
state->program->data = state->program->code + code_size + 1;
state->program->code = safe_malloc(sizeof(BC_WORD) * (code_size+state->program->data_size));
state->program->data = state->program->code + code_size;
#endif
if (provide_chars(&elem32, sizeof(elem32), 1, cp) < 0)
......@@ -540,7 +538,7 @@ int parse_program(struct parser *state, struct char_provider *cp) {
# ifdef INTERPRETER
state->program->symbol_table[state->ptr].offset += (BC_WORD) state->program->data;
# elif defined(PRELINKER)
state->program->symbol_table[state->ptr].offset += ((BC_WORD)state->program->code_size+3)*8+sizeof(prelinker_preamble);
state->program->symbol_table[state->ptr].offset += ((BC_WORD)state->program->code_size)*8+sizeof(prelinker_preamble);
# endif
# ifdef LINK_CLEAN_RUNTIME
if (state->program->symbol_table[state->ptr].name[0]) {
......@@ -577,7 +575,7 @@ int parse_program(struct parser *state, struct char_provider *cp) {
# ifdef INTERPRETER
state->program->symbol_table[state->ptr].offset += (BC_WORD) state->program->code;
# elif defined(PRELINKER)
state->program->symbol_table[state->ptr].offset += 2*8+sizeof(prelinker_preamble);
state->program->symbol_table[state->ptr].offset += sizeof(prelinker_preamble);
# endif
# ifdef LINK_CLEAN_RUNTIME
if (state->program->symbol_table[state->ptr].name[0]) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment