Compare commits
6 Commits
main
...
956d42d008
| Author | SHA1 | Date | |
|---|---|---|---|
|
956d42d008
|
|||
|
87d8345017
|
|||
|
d39fe580fc
|
|||
|
ba8c99a123
|
|||
|
cc8ae742f0
|
|||
|
812443d6ee
|
29
asm.js
29
asm.js
@@ -187,6 +187,9 @@ const opcodes = {
|
||||
"i32.shl": 0x74,
|
||||
"i32.shr_s": 0x75,
|
||||
"i32.shr_u": 0x76,
|
||||
"i64.or": 0x84,
|
||||
"i64.shl": 0x86,
|
||||
"i64.extend_i32_u": 0xad,
|
||||
|
||||
// Threads instructions
|
||||
"memory.atomic.notify": [ 0xfe, 0x00 ],
|
||||
@@ -194,6 +197,7 @@ const opcodes = {
|
||||
"i32.atomic.load": [ 0xfe, 0x10 ],
|
||||
"i32.atomic.load8_u": [ 0xfe, 0x12 ],
|
||||
"i32.atomic.store": [ 0xfe, 0x17 ],
|
||||
"i64.atomic.store": [ 0xfe, 0x18 ],
|
||||
"i32.atomic.store8": [ 0xfe, 0x19 ],
|
||||
};
|
||||
|
||||
@@ -888,6 +892,7 @@ const Section = Object.freeze({
|
||||
const Kind = Object.freeze({
|
||||
FUNC: 0x00,
|
||||
MEM: 0x02,
|
||||
GLOBAL: 0x03,
|
||||
});
|
||||
|
||||
export class Assembler {
|
||||
@@ -942,8 +947,28 @@ export class Assembler {
|
||||
}
|
||||
|
||||
action_export(action) {
|
||||
const index = Object.keys(this.funcs).indexOf(action.name);
|
||||
this.exports[action.name] = { kind: Kind.FUNC, index };
|
||||
const func_index = Object.keys(this.funcs).indexOf(action.name);
|
||||
if (func_index != -1) {
|
||||
this.exports[action.name] = {
|
||||
kind: Kind.FUNC,
|
||||
index: func_index,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const global_index = Object.keys(this.globals).indexOf(action.name);
|
||||
if (global_index != -1) {
|
||||
this.exports[action.name] = {
|
||||
kind: Kind.GLOBAL,
|
||||
index: global_index,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(
|
||||
`ERROR: Unable to resolve export ${action.name} `
|
||||
+ "(only functions and globals currently supported)"
|
||||
);
|
||||
}
|
||||
|
||||
action_func(action) {
|
||||
|
||||
15
emu.js
15
emu.js
@@ -51,6 +51,19 @@ class Emulator {
|
||||
document.addEventListener('keydown', (e) => this.handle_keydown(e));
|
||||
window.addEventListener('resize', () => this.handle_resize());
|
||||
|
||||
this.prof = new Worker("prof.js");
|
||||
this.prof.onmessage = (e) => {
|
||||
const blob = new Blob(
|
||||
[JSON.stringify(e.data)],
|
||||
{ type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "wipforth-profile.json";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
this.print("Assembling kernel ");
|
||||
const dots = setInterval(() => this.print("."), DOT_INTERVAL_MS);
|
||||
this.worker = new Worker('boot.js', { type: 'module' });
|
||||
@@ -59,6 +72,7 @@ class Emulator {
|
||||
clearInterval(dots);
|
||||
this.print(" done\n");
|
||||
this.worker.postMessage({ type: "boot" });
|
||||
this.prof.postMessage({ type: "start", mem: this.mem });
|
||||
};
|
||||
|
||||
fetch('prelude.f')
|
||||
@@ -85,6 +99,7 @@ class Emulator {
|
||||
if (!this.input_enable) {
|
||||
const sysready = Atomics.load(this.mem_u8, SYSREADY);
|
||||
if (sysready != 0) {
|
||||
this.prof.postMessage({ type: "stop" });
|
||||
this.input_enable = true;
|
||||
this.flush_output();
|
||||
document.getElementById('cursor').classList.add('blinking');
|
||||
|
||||
48
prof.js
Normal file
48
prof.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const INTERVAL_MS = 1;
|
||||
const RS_TOP_ADDR = 0x10000;
|
||||
const PROF_DATA_ADDR = 0x58;
|
||||
const PROF_DATA_IDX = PROF_DATA_ADDR / 8;
|
||||
|
||||
let mem_8;
|
||||
let mem_64;
|
||||
let sampler;
|
||||
|
||||
const samples = [];
|
||||
|
||||
function sample() {
|
||||
const data = Atomics.load(mem_64, PROF_DATA_IDX);
|
||||
const ip = Number(data & 0xffffffffn);
|
||||
const rsp = Number(data >> 32n);
|
||||
samples.push({ ip, rs_bytes: mem_8.slice(rsp, RS_TOP_ADDR) });
|
||||
}
|
||||
|
||||
function i32(bytes) {
|
||||
return bytes[0]
|
||||
| (bytes[1] << 8)
|
||||
| (bytes[2] << 16)
|
||||
| (bytes[3] << 24);
|
||||
}
|
||||
function postproc({ ip, rs_bytes }) {
|
||||
const rs = [];
|
||||
for (let i = 0; i < rs_bytes.length; i += 4)
|
||||
rs.push(i32(rs_bytes.slice(i, i + 4)));
|
||||
rs.reverse();
|
||||
return { ip, rs };
|
||||
}
|
||||
|
||||
self.onmessage = (e) => {
|
||||
switch (e.data.type) {
|
||||
case "start":
|
||||
console.log("Starting profiler");
|
||||
mem_8 = new Uint8Array(e.data.mem.buffer);
|
||||
mem_64 = new BigUint64Array(e.data.mem.buffer);
|
||||
ip = e.data.ip;
|
||||
rsp = e.data.rsp;
|
||||
sampler = setInterval(sample, INTERVAL_MS);
|
||||
break;
|
||||
case "stop":
|
||||
clearInterval(sample);
|
||||
console.log("Stopped profiler");
|
||||
self.postMessage(samples.map(postproc));
|
||||
}
|
||||
};
|
||||
10
wipforth.ws
10
wipforth.ws
@@ -9,6 +9,9 @@
|
||||
.def RXHEAD 48h
|
||||
.def RXTAIL 4Ch
|
||||
|
||||
;; Mirror of registers for profiler to sample
|
||||
.def PROF_DATA 58h
|
||||
|
||||
.def DICT_START 0200h
|
||||
|
||||
.def RSP_INIT 10000h
|
||||
@@ -2073,6 +2076,13 @@ KERNEL_DEFS_END:
|
||||
.func trampoline
|
||||
loop iter
|
||||
global.get fn call_indirect codeword codewords
|
||||
|
||||
i32.const PROF_DATA
|
||||
global.get ip i64.extend_i32_u
|
||||
global.get rsp i64.extend_i32_u
|
||||
i64.const 32 i64.shl i64.or
|
||||
i64.atomic.store 3 0
|
||||
|
||||
global.get run br_if iter
|
||||
end
|
||||
|
||||
|
||||
16
words.js
Normal file
16
words.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Assembler } from "./asm.js";
|
||||
|
||||
const asm = new Assembler();
|
||||
for await (const chunk of Deno.stdin.readable) {
|
||||
asm.push(chunk);
|
||||
}
|
||||
asm.wasm();
|
||||
|
||||
const defs = Object.entries(asm.defs);
|
||||
while (defs[0][0] != '_DUP')
|
||||
defs.shift();
|
||||
while (defs.at(-1)[0] != 'WNF_HANDLER')
|
||||
defs.pop();
|
||||
const words = Object.fromEntries(defs.filter(([k,v]) => !k.startsWith("_")));
|
||||
|
||||
console.log(JSON.stringify(words));
|
||||
Reference in New Issue
Block a user