Compare commits
6 Commits
aa9c18346d
...
profiler
| 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.shl": 0x74,
|
||||||
"i32.shr_s": 0x75,
|
"i32.shr_s": 0x75,
|
||||||
"i32.shr_u": 0x76,
|
"i32.shr_u": 0x76,
|
||||||
|
"i64.or": 0x84,
|
||||||
|
"i64.shl": 0x86,
|
||||||
|
"i64.extend_i32_u": 0xad,
|
||||||
|
|
||||||
// Threads instructions
|
// Threads instructions
|
||||||
"memory.atomic.notify": [ 0xfe, 0x00 ],
|
"memory.atomic.notify": [ 0xfe, 0x00 ],
|
||||||
@@ -194,6 +197,7 @@ const opcodes = {
|
|||||||
"i32.atomic.load": [ 0xfe, 0x10 ],
|
"i32.atomic.load": [ 0xfe, 0x10 ],
|
||||||
"i32.atomic.load8_u": [ 0xfe, 0x12 ],
|
"i32.atomic.load8_u": [ 0xfe, 0x12 ],
|
||||||
"i32.atomic.store": [ 0xfe, 0x17 ],
|
"i32.atomic.store": [ 0xfe, 0x17 ],
|
||||||
|
"i64.atomic.store": [ 0xfe, 0x18 ],
|
||||||
"i32.atomic.store8": [ 0xfe, 0x19 ],
|
"i32.atomic.store8": [ 0xfe, 0x19 ],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -888,6 +892,7 @@ const Section = Object.freeze({
|
|||||||
const Kind = Object.freeze({
|
const Kind = Object.freeze({
|
||||||
FUNC: 0x00,
|
FUNC: 0x00,
|
||||||
MEM: 0x02,
|
MEM: 0x02,
|
||||||
|
GLOBAL: 0x03,
|
||||||
});
|
});
|
||||||
|
|
||||||
export class Assembler {
|
export class Assembler {
|
||||||
@@ -942,8 +947,28 @@ export class Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
action_export(action) {
|
action_export(action) {
|
||||||
const index = Object.keys(this.funcs).indexOf(action.name);
|
const func_index = Object.keys(this.funcs).indexOf(action.name);
|
||||||
this.exports[action.name] = { kind: Kind.FUNC, index };
|
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) {
|
action_func(action) {
|
||||||
|
|||||||
15
emu.js
15
emu.js
@@ -51,6 +51,19 @@ class Emulator {
|
|||||||
document.addEventListener('keydown', (e) => this.handle_keydown(e));
|
document.addEventListener('keydown', (e) => this.handle_keydown(e));
|
||||||
window.addEventListener('resize', () => this.handle_resize());
|
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 ");
|
this.print("Assembling kernel ");
|
||||||
const dots = setInterval(() => this.print("."), DOT_INTERVAL_MS);
|
const dots = setInterval(() => this.print("."), DOT_INTERVAL_MS);
|
||||||
this.worker = new Worker('boot.js', { type: 'module' });
|
this.worker = new Worker('boot.js', { type: 'module' });
|
||||||
@@ -59,6 +72,7 @@ class Emulator {
|
|||||||
clearInterval(dots);
|
clearInterval(dots);
|
||||||
this.print(" done\n");
|
this.print(" done\n");
|
||||||
this.worker.postMessage({ type: "boot" });
|
this.worker.postMessage({ type: "boot" });
|
||||||
|
this.prof.postMessage({ type: "start", mem: this.mem });
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch('prelude.f')
|
fetch('prelude.f')
|
||||||
@@ -85,6 +99,7 @@ class Emulator {
|
|||||||
if (!this.input_enable) {
|
if (!this.input_enable) {
|
||||||
const sysready = Atomics.load(this.mem_u8, SYSREADY);
|
const sysready = Atomics.load(this.mem_u8, SYSREADY);
|
||||||
if (sysready != 0) {
|
if (sysready != 0) {
|
||||||
|
this.prof.postMessage({ type: "stop" });
|
||||||
this.input_enable = true;
|
this.input_enable = true;
|
||||||
this.flush_output();
|
this.flush_output();
|
||||||
document.getElementById('cursor').classList.add('blinking');
|
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 RXHEAD 48h
|
||||||
.def RXTAIL 4Ch
|
.def RXTAIL 4Ch
|
||||||
|
|
||||||
|
;; Mirror of registers for profiler to sample
|
||||||
|
.def PROF_DATA 58h
|
||||||
|
|
||||||
.def DICT_START 0200h
|
.def DICT_START 0200h
|
||||||
|
|
||||||
.def RSP_INIT 10000h
|
.def RSP_INIT 10000h
|
||||||
@@ -2073,6 +2076,13 @@ KERNEL_DEFS_END:
|
|||||||
.func trampoline
|
.func trampoline
|
||||||
loop iter
|
loop iter
|
||||||
global.get fn call_indirect codeword codewords
|
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
|
global.get run br_if iter
|
||||||
end
|
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