Skip to content

Commit

Permalink
Add a generator for a whamm hotness monitor
Browse files Browse the repository at this point in the history
  • Loading branch information
ejrgilbert committed Dec 11, 2024
1 parent eb66016 commit 5b07f24
Show file tree
Hide file tree
Showing 6 changed files with 2,092 additions and 347 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
var MON_NAME = "hotness-mon.wat";
var TABS = 4;

def main(args: Array<string>) -> int {
var fd: int;
if (args.length != 1) {
System.puts("Check gen_hotness-mon.sh, must pass workdir arg!\n");
return 1;
} else {
var workdir = args[0];
var path = StringBuilder.new().put2("%s/%s", workdir, MON_NAME);
fd = System.fileOpen(path.toString(), false);
}

var wat = WatWriter.new(TABS);

write_docs(wat);
wat.module_start();
setup(wat);

var ran_select = false;
for (opcode in Opcode) {
var mnemonic = opcode.mnemonic;

if (Strings.equal(mnemonic,"<invalid>")) {
continue;
}
if (Strings.equal(mnemonic,"select")) {
if (!ran_select) {
ran_select = true;
} else {
// skip second select opcode (0x1B and 0x1C)
continue;
}
}
gen_probe(wat, mnemonic);
}

alloc_func(wat);
count_func(wat);
flush_func(wat);

wat.module_end();

// Output wat to file
var str = wat.str();
System.fileWriteK(fd, str, 0, str.length);
System.fileClose(fd);
return 0;
}

def gen_probe(wat: WatWriter, opcode_name: string) {
var sig = StringBuilder.new().put1("(export \"wasm:opcode:%s ($alloc(fid, pc))\") (param $entry i32)", opcode_name).toString();
wat.func_start(sig)
.w("(call $count_probe (local.get $entry))")
.func_end();
}

def write_docs(wat: WatWriter) {
wat.ws([";; === Whamm Hotness Monitor ===",
";; Instrument EVERY location in the application and track each",
";; time it is executed dynamically with an in-memory counter.\n",
";; --> Output format: <fid>, pc=<pc>, [<counter>]\n"]);
}

def setup(wat: WatWriter) {
// imports
wat.ws(["(import \"wizeng\" \"puti\" (func $puti (param i32)))",
"(import \"wizeng\" \"puts\" (func $puts (param i32 i32)))\n",

// memory/globals
"(memory (export \"mem\") 2) ;; no expansion checks",
"(global $last_entry (mut i32) (i32.const 0))\n",

// data(data (i32.const 0xc00) "func=")
"(data (i32.const 0xd00) \", pc=\")",
"(data (i32.const 0xe00) \"\\n\")",
"(data (i32.const 0xf00) \", [\")",
"(data (i32.const 0xf10) \",\")",
"(data (i32.const 0xf20) \"]\")\n"]);
}

def alloc_func(wat: WatWriter) {
wat.func_start("(export \"$alloc\") (param $func i32) (param $pc i32) (result i32)")
.ws(["(local $entry i32)\n",

"global.get $last_entry",
"local.set $entry\n",

"local.get $entry",
"local.get $func",
"i32.store\n",

"local.get $entry",
"local.get $pc",
"i32.store offset=4\n",

"local.get $entry",
"i32.const 1",
"i32.store offset=8\n",

"local.get $entry",
"i32.const 20",
"i32.add",
"global.set $last_entry\n",

"local.get $entry"])
.func_end();
}

def count_func(wat: WatWriter) {
wat.func_start("$count_probe (param $entry i32)")
.ws(["local.get $entry",
"local.get $entry",
"i64.load offset=12 ;; count number of times fired",
"i64.const 1",
"i64.add",
"i64.store offset=12"])
.func_end();
}

def flush_func(wat: WatWriter) {
wat.func_start("$flush (export \"wasm:exit\")")
.ws(["(local $entry i32)",
"(local $options i32)",
"(block $end_loop"]).indent()
.w("(loop $loop_entry").indent()
.ws([";; check at the end of memory",
"local.get $entry",
"global.get $last_entry",
"i32.eq",
"br_if $end_loop\n",

"(call $puts (i32.const 0xc00) (i32.const 5))",
"local.get $entry",
"i32.load ;; func",
"call $puti",
"local.get $entry",
"i32.const 4",
"i32.add",
"local.set $entry\n",

"(call $puts (i32.const 0xd00) (i32.const 5))",
"local.get $entry",
"i32.load ;; pc",
"call $puti",
"local.get $entry",
"i32.const 4",
"i32.add",
"local.set $entry\n",

"local.get $entry",
"i32.load ;; number of options",
"local.get $entry",
"i32.const 4",
"i32.add",
"local.set $entry\n",

"local.set $options\n",

"(call $puts (i32.const 0xf00) (i32.const 3))\n",

"(loop $loop_options"]).indent()
.ws(["local.get $entry",
"i32.load",
"call $puti\n",

"local.get $entry",
"i32.const 8",
"i32.add",
"local.set $entry\n",

"local.get $options",
"i32.const -1",
"i32.add\n",

"local.tee $options\n",

"i32.eqz",
"(if"]).indent()
.w("(then").indent()
.w("(call $puts (i32.const 0xf20) (i32.const 1))").dedent()
.ws([")",
"(else"]).indent()
.ws(["(call $puts (i32.const 0xf10) (i32.const 1))",
"br $loop_options"]).dedent()
.w(")").dedent()
.w(")").dedent()
.ws([")\n",
"(call $puts (i32.const 0xe00) (i32.const 1))",
"br $loop_entry"]).dedent()
.w(")").dedent()
.w(")")
.func_end();
}

class WatWriter(spaces_per_tab: int) {
var buf = StringBuilder.new();
var curr_indent = 0;

// Functions
def func_start(sig: string) -> WatWriter {
sexp_start("func ", false);
if (sig != null) {
buf.puts(sig);
}
nl();
indent();
return this;
}
def func_end() -> WatWriter {
dedent();
sexp_end();
return this;
}

// Modules
def module_start() -> WatWriter {
sexp_start("module", true);
indent();
return this;
}
def module_end() -> WatWriter {
reset_indent();
sexp_end();
return this;
}

// S-Expressions
def sexp_start(name: string, wnl: bool) -> WatWriter {
w_nonl(StringBuilder.new().put1("(%s", name).toString());
if (wnl) {
nl();
}
return this;
}
def sexp_end() -> WatWriter {
w(")");
return this;
}

// Writing logic
private def w_nonl(s: string) {
w2("%s%s", gen_tabs(), s);
}
def w(s: string) -> WatWriter {
w_nonl(s);
nl();
return this;
}
def ws(a: Array<string>) -> WatWriter {
for (str in a) {
w(str);
}

return this;
}
def w1(fmt: string, s: string) -> WatWriter {
buf.put1(fmt, s);
return this;
}
def w2(fmt: string, s0: string, s1: string) -> WatWriter {
buf.put2(fmt, s0, s1);
return this;
}
private def nl() -> WatWriter {
buf.putc('\n');
return this;
}

// Tabbing logic
def indent() -> WatWriter {
curr_indent++;
return this;
}
def dedent() -> WatWriter {
curr_indent--;
return this;
}
def reset_indent() -> WatWriter {
curr_indent = 0;
return this;
}
private def gen_tabs() -> string {
var tabs = StringBuilder.new();
for (i < curr_indent * spaces_per_tab) {
tabs.putc(' ');
}
return tabs.toString();
}

def str() -> string {
return buf.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ fi
ENGINE="$SRC_DIR/engine/*.v3 $SRC_DIR/util/*.v3 $VIRGIL_LIB/util/*.v3"
TARGET_V3="$SRC_DIR/engine/v3/*.v3"

v3i $ENGINE $TARGET_V3 $DIR/*.v3
if ! v3i $ENGINE $TARGET_V3 $DIR/*.v3 $DIR; then
exit 1
fi

wat2wasm $DIR/hotness-mon.wat -o $DIR/hotness-mon.wasm
mv $DIR/*.wa* $DIR/../..
Loading

0 comments on commit 5b07f24

Please sign in to comment.