From 483d759230f4693abfca3a74a97b1c1db6d2a0d6 Mon Sep 17 00:00:00 2001 From: Daniel Wirtz Date: Mon, 20 Apr 2020 23:01:26 +0200 Subject: Refactor expression runner so it can be used via the C and JS APIs (#2702) Refactors most of the precompute pass's expression runner into its base class so it can also be used via the C and JS APIs. Also adds the option to populate the runner with known constant local and global values upfront, and remembers assigned intermediate values as well as traversing into functions if requested. --- test/binaryen.js/custom-section.js.txt | 1 + test/binaryen.js/expressionrunner.js | 208 ++++++++++++++++++++++++++++++ test/binaryen.js/expressionrunner.js.txt | 161 +++++++++++++++++++++++ test/binaryen.js/inlining-options.js.txt | 1 + test/binaryen.js/kitchen-sink.js.txt | 3 + test/binaryen.js/low-memory-unused.js.txt | 1 + test/binaryen.js/pass-arguments.js.txt | 1 + 7 files changed, 376 insertions(+) create mode 100644 test/binaryen.js/expressionrunner.js create mode 100644 test/binaryen.js/expressionrunner.js.txt (limited to 'test/binaryen.js') diff --git a/test/binaryen.js/custom-section.js.txt b/test/binaryen.js/custom-section.js.txt index 063ef9dde..7c68582f1 100644 --- a/test/binaryen.js/custom-section.js.txt +++ b/test/binaryen.js/custom-section.js.txt @@ -10,6 +10,7 @@ int main() { std::map events; std::map exports; std::map relooperBlocks; + std::map expressionRunners; BinaryenModuleRef the_module = NULL; RelooperRef the_relooper = NULL; the_module = BinaryenModuleCreate(); diff --git a/test/binaryen.js/expressionrunner.js b/test/binaryen.js/expressionrunner.js new file mode 100644 index 000000000..35117c453 --- /dev/null +++ b/test/binaryen.js/expressionrunner.js @@ -0,0 +1,208 @@ +var Flags = binaryen.ExpressionRunner.Flags; +console.log("// ExpressionRunner.Flags.Default = " + Flags.Default); +console.log("// ExpressionRunner.Flags.PreserveSideeffects = " + Flags.PreserveSideeffects); +console.log("// ExpressionRunner.Flags.TraverseCalls = " + Flags.TraverseCalls); + +binaryen.setAPITracing(true); + +function assertDeepEqual(x, y) { + if (typeof x === "object") { + for (let i in x) assertDeepEqual(x[i], y[i]); + for (let i in y) assertDeepEqual(x[i], y[i]); + } else { + assert(x === y); + } +} + +var module = new binaryen.Module(); +module.addGlobal("aGlobal", binaryen.i32, true, module.i32.const(0)); + +// Should evaluate down to a constant +var runner = new binaryen.ExpressionRunner(module); +var expr = runner.runAndDispose( + module.i32.add( + module.i32.const(1), + module.i32.const(2) + ) +); +assertDeepEqual( + binaryen.getExpressionInfo(expr), + { + id: binaryen.ExpressionIds.Const, + type: binaryen.i32, + value: 3 + } +); + +// Should traverse control structures +runner = new binaryen.ExpressionRunner(module); +expr = runner.runAndDispose( + module.i32.add( + module.i32.const(1), + module.if( + module.i32.const(0), + module.i32.const(0), + module.i32.const(3) + ) + ), +); +assertDeepEqual( + binaryen.getExpressionInfo(expr), + { + id: binaryen.ExpressionIds.Const, + type: binaryen.i32, + value: 4 + } +); + +// Should be unable to evaluate a local if not explicitly specified +runner = new binaryen.ExpressionRunner(module); +expr = runner.runAndDispose( + module.i32.add( + module.local.get(0, binaryen.i32), + module.i32.const(1) + ) +); +assert(expr === 0); + +// Should handle traps properly +runner = new binaryen.ExpressionRunner(module); +expr = runner.runAndDispose( + module.unreachable() +); +assert(expr === 0); + +// Should ignore `local.tee` side-effects if just evaluating the expression +runner = new binaryen.ExpressionRunner(module); +expr = runner.runAndDispose( + module.i32.add( + module.local.tee(0, module.i32.const(4), binaryen.i32), + module.i32.const(1) + ) +); +assertDeepEqual( + binaryen.getExpressionInfo(expr), + { + id: binaryen.ExpressionIds.Const, + type: binaryen.i32, + value: 5 + } +); + +// Should preserve any side-effects if explicitly requested +runner = new binaryen.ExpressionRunner(module, Flags.PreserveSideeffects); +expr = runner.runAndDispose( + module.i32.add( + module.local.tee(0, module.i32.const(4), binaryen.i32), + module.i32.const(1) + ) +); +assert(expr === 0); + +// Should work with temporary values if just evaluating the expression +runner = new binaryen.ExpressionRunner(module); +expr = runner.runAndDispose( + module.i32.add( + module.block(null, [ + module.local.set(0, module.i32.const(2)), + module.local.get(0, binaryen.i32) + ], binaryen.i32), + module.block(null, [ + module.global.set("aGlobal", module.i32.const(4)), + module.global.get("aGlobal", binaryen.i32) + ], binaryen.i32) + ) +); +assertDeepEqual( + binaryen.getExpressionInfo(expr), + { + id: binaryen.ExpressionIds.Const, + type: binaryen.i32, + value: 6 + } +); + +// Should pick up explicitly preset values +runner = new binaryen.ExpressionRunner(module, Flags.PreserveSideeffects); +assert(runner.setLocalValue(0, module.i32.const(3))); +assert(runner.setGlobalValue("aGlobal", module.i32.const(4))); +expr = runner.runAndDispose( + module.i32.add( + module.local.get(0, binaryen.i32), + module.global.get("aGlobal", binaryen.i32) + ) +); +assertDeepEqual( + binaryen.getExpressionInfo(expr), + { + id: binaryen.ExpressionIds.Const, + type: binaryen.i32, + value: 7 + } +); + +// Should traverse into (simple) functions if requested +runner = new binaryen.ExpressionRunner(module, Flags.TraverseCalls); +module.addFunction("add", binaryen.createType([ binaryen.i32, binaryen.i32 ]), binaryen.i32, [], + module.block(null, [ + module.i32.add( + module.local.get(0, binaryen.i32), + module.local.get(1, binaryen.i32) + ) + ], binaryen.i32) +); +assert(runner.setLocalValue(0, module.i32.const(1))); +expr = runner.runAndDispose( + module.i32.add( + module.i32.add( + module.local.get(0, binaryen.i32), + module.call("add", [ + module.i32.const(2), + module.i32.const(4) + ], binaryen.i32) + ), + module.local.get(0, binaryen.i32) + ) +); +assertDeepEqual( + binaryen.getExpressionInfo(expr), + { + id: binaryen.ExpressionIds.Const, + type: binaryen.i32, + value: 8 + } +); + +// Should not attempt to traverse into functions if not explicitly set +runner = new binaryen.ExpressionRunner(module); +expr = runner.runAndDispose( + module.i32.add( + module.i32.const(1), + module.call("add", [ + module.i32.const(3), + module.i32.const(4) + ], binaryen.i32) + ) +); +assert(expr === 0); + +// Should stop on maxDepth +runner = new binaryen.ExpressionRunner(module, Flags.Default, 1); +expr = runner.runAndDispose( + module.block(null, [ + module.i32.const(1), + ], binaryen.i32) +); +assert(expr === 0); + +// Should not loop infinitely +runner = new binaryen.ExpressionRunner(module, Flags.Default, 50, 3); +expr = runner.runAndDispose( + module.loop("theLoop", + module.br("theLoop") + ) +); +assert(expr === 0); + +module.dispose(); +binaryen.setAPITracing(false); diff --git a/test/binaryen.js/expressionrunner.js.txt b/test/binaryen.js/expressionrunner.js.txt new file mode 100644 index 000000000..4d74c9feb --- /dev/null +++ b/test/binaryen.js/expressionrunner.js.txt @@ -0,0 +1,161 @@ +// ExpressionRunner.Flags.Default = 0 +// ExpressionRunner.Flags.PreserveSideeffects = 1 +// ExpressionRunner.Flags.TraverseCalls = 2 +// beginning a Binaryen API trace +#include +#include +#include "binaryen-c.h" +int main() { + std::map types; + std::map expressions; + std::map functions; + std::map globals; + std::map events; + std::map exports; + std::map relooperBlocks; + std::map expressionRunners; + BinaryenModuleRef the_module = NULL; + RelooperRef the_relooper = NULL; + the_module = BinaryenModuleCreate(); + expressions[size_t(NULL)] = BinaryenExpressionRef(NULL); + expressions[1] = BinaryenConst(the_module, BinaryenLiteralInt32(0)); + globals[0] = BinaryenAddGlobal(the_module, "aGlobal", BinaryenTypeInt32(), 1, expressions[1]); + expressionRunners[0] = ExpressionRunnerCreate(the_module, 0, 0, 0); + expressions[2] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + expressions[3] = BinaryenConst(the_module, BinaryenLiteralInt32(2)); + expressions[4] = BinaryenBinary(the_module, 0, expressions[2], expressions[3]); + expressions[5] = ExpressionRunnerRunAndDispose(expressionRunners[0], expressions[4]); + BinaryenExpressionGetId(expressions[5]); + BinaryenExpressionGetType(expressions[5]); + BinaryenConstGetValueI32(expressions[5]); + expressionRunners[1] = ExpressionRunnerCreate(the_module, 0, 0, 0); + expressions[6] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + expressions[7] = BinaryenConst(the_module, BinaryenLiteralInt32(0)); + expressions[8] = BinaryenConst(the_module, BinaryenLiteralInt32(0)); + expressions[9] = BinaryenConst(the_module, BinaryenLiteralInt32(3)); + expressions[10] = BinaryenIf(the_module, expressions[7], expressions[8], expressions[9]); + expressions[11] = BinaryenBinary(the_module, 0, expressions[6], expressions[10]); + expressions[12] = ExpressionRunnerRunAndDispose(expressionRunners[1], expressions[11]); + BinaryenExpressionGetId(expressions[12]); + BinaryenExpressionGetType(expressions[12]); + BinaryenConstGetValueI32(expressions[12]); + expressionRunners[2] = ExpressionRunnerCreate(the_module, 0, 0, 0); + expressions[13] = BinaryenLocalGet(the_module, 0, BinaryenTypeInt32()); + expressions[14] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + expressions[15] = BinaryenBinary(the_module, 0, expressions[13], expressions[14]); + ExpressionRunnerRunAndDispose(expressionRunners[2], expressions[15]); + expressionRunners[3] = ExpressionRunnerCreate(the_module, 0, 0, 0); + expressions[16] = BinaryenUnreachable(the_module); + ExpressionRunnerRunAndDispose(expressionRunners[3], expressions[16]); + expressionRunners[4] = ExpressionRunnerCreate(the_module, 0, 0, 0); + expressions[17] = BinaryenConst(the_module, BinaryenLiteralInt32(4)); + expressions[18] = BinaryenLocalTee(the_module, 0, expressions[17], BinaryenTypeInt32()); + expressions[19] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + expressions[20] = BinaryenBinary(the_module, 0, expressions[18], expressions[19]); + expressions[21] = ExpressionRunnerRunAndDispose(expressionRunners[4], expressions[20]); + BinaryenExpressionGetId(expressions[21]); + BinaryenExpressionGetType(expressions[21]); + BinaryenConstGetValueI32(expressions[21]); + expressionRunners[5] = ExpressionRunnerCreate(the_module, 1, 0, 0); + expressions[22] = BinaryenConst(the_module, BinaryenLiteralInt32(4)); + expressions[23] = BinaryenLocalTee(the_module, 0, expressions[22], BinaryenTypeInt32()); + expressions[24] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + expressions[25] = BinaryenBinary(the_module, 0, expressions[23], expressions[24]); + ExpressionRunnerRunAndDispose(expressionRunners[5], expressions[25]); + expressionRunners[6] = ExpressionRunnerCreate(the_module, 0, 0, 0); + expressions[26] = BinaryenConst(the_module, BinaryenLiteralInt32(2)); + expressions[27] = BinaryenLocalSet(the_module, 0, expressions[26]); + expressions[28] = BinaryenLocalGet(the_module, 0, BinaryenTypeInt32()); + { + BinaryenExpressionRef children[] = { expressions[27], expressions[28] }; + expressions[29] = BinaryenBlock(the_module, NULL, children, 2, BinaryenTypeInt32()); + } + expressions[30] = BinaryenConst(the_module, BinaryenLiteralInt32(4)); + expressions[31] = BinaryenGlobalSet(the_module, "aGlobal", expressions[30]); + expressions[32] = BinaryenGlobalGet(the_module, "aGlobal", BinaryenTypeInt32()); + { + BinaryenExpressionRef children[] = { expressions[31], expressions[32] }; + expressions[33] = BinaryenBlock(the_module, NULL, children, 2, BinaryenTypeInt32()); + } + expressions[34] = BinaryenBinary(the_module, 0, expressions[29], expressions[33]); + expressions[35] = ExpressionRunnerRunAndDispose(expressionRunners[6], expressions[34]); + BinaryenExpressionGetId(expressions[35]); + BinaryenExpressionGetType(expressions[35]); + BinaryenConstGetValueI32(expressions[35]); + expressionRunners[7] = ExpressionRunnerCreate(the_module, 1, 0, 0); + expressions[36] = BinaryenConst(the_module, BinaryenLiteralInt32(3)); + ExpressionRunnerSetLocalValue(expressionRunners[7], 0, expressions[36]); + expressions[37] = BinaryenConst(the_module, BinaryenLiteralInt32(4)); + ExpressionRunnerSetGlobalValue(expressionRunners[7], "aGlobal", expressions[37]); + expressions[38] = BinaryenLocalGet(the_module, 0, BinaryenTypeInt32()); + expressions[39] = BinaryenGlobalGet(the_module, "aGlobal", BinaryenTypeInt32()); + expressions[40] = BinaryenBinary(the_module, 0, expressions[38], expressions[39]); + expressions[41] = ExpressionRunnerRunAndDispose(expressionRunners[7], expressions[40]); + BinaryenExpressionGetId(expressions[41]); + BinaryenExpressionGetType(expressions[41]); + BinaryenConstGetValueI32(expressions[41]); + expressionRunners[8] = ExpressionRunnerCreate(the_module, 2, 0, 0); + { + BinaryenType t0[] = {BinaryenTypeInt32(), BinaryenTypeInt32()}; + types[0] = BinaryenTypeCreate(t0, 2); + } + expressions[42] = BinaryenLocalGet(the_module, 0, BinaryenTypeInt32()); + expressions[43] = BinaryenLocalGet(the_module, 1, BinaryenTypeInt32()); + expressions[44] = BinaryenBinary(the_module, 0, expressions[42], expressions[43]); + { + BinaryenExpressionRef children[] = { expressions[44] }; + expressions[45] = BinaryenBlock(the_module, NULL, children, 1, BinaryenTypeInt32()); + } + { + BinaryenType varTypes[] = { 0 }; + functions[0] = BinaryenAddFunction(the_module, "add", types[0], BinaryenTypeInt32(), varTypes, 0, expressions[45]); + } + expressions[46] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + ExpressionRunnerSetLocalValue(expressionRunners[8], 0, expressions[46]); + expressions[47] = BinaryenLocalGet(the_module, 0, BinaryenTypeInt32()); + expressions[48] = BinaryenConst(the_module, BinaryenLiteralInt32(2)); + expressions[49] = BinaryenConst(the_module, BinaryenLiteralInt32(4)); + { + BinaryenExpressionRef operands[] = { expressions[48], expressions[49] }; + expressions[50] = BinaryenCall(the_module, "add", operands, 2, BinaryenTypeInt32()); + } + expressions[51] = BinaryenBinary(the_module, 0, expressions[47], expressions[50]); + expressions[52] = BinaryenLocalGet(the_module, 0, BinaryenTypeInt32()); + expressions[53] = BinaryenBinary(the_module, 0, expressions[51], expressions[52]); + expressions[54] = ExpressionRunnerRunAndDispose(expressionRunners[8], expressions[53]); + BinaryenExpressionGetId(expressions[54]); + BinaryenExpressionGetType(expressions[54]); + BinaryenConstGetValueI32(expressions[54]); + expressionRunners[9] = ExpressionRunnerCreate(the_module, 0, 0, 0); + expressions[55] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + expressions[56] = BinaryenConst(the_module, BinaryenLiteralInt32(3)); + expressions[57] = BinaryenConst(the_module, BinaryenLiteralInt32(4)); + { + BinaryenExpressionRef operands[] = { expressions[56], expressions[57] }; + expressions[58] = BinaryenCall(the_module, "add", operands, 2, BinaryenTypeInt32()); + } + expressions[59] = BinaryenBinary(the_module, 0, expressions[55], expressions[58]); + ExpressionRunnerRunAndDispose(expressionRunners[9], expressions[59]); + expressionRunners[10] = ExpressionRunnerCreate(the_module, 0, 1, 0); + expressions[60] = BinaryenConst(the_module, BinaryenLiteralInt32(1)); + { + BinaryenExpressionRef children[] = { expressions[60] }; + expressions[61] = BinaryenBlock(the_module, NULL, children, 1, BinaryenTypeInt32()); + } + ExpressionRunnerRunAndDispose(expressionRunners[10], expressions[61]); + expressionRunners[11] = ExpressionRunnerCreate(the_module, 0, 50, 3); + expressions[62] = BinaryenBreak(the_module, "theLoop", expressions[0], expressions[0]); + expressions[63] = BinaryenLoop(the_module, "theLoop", expressions[62]); + ExpressionRunnerRunAndDispose(expressionRunners[11], expressions[63]); + BinaryenModuleDispose(the_module); + types.clear(); + expressions.clear(); + functions.clear(); + globals.clear(); + events.clear(); + exports.clear(); + relooperBlocks.clear(); + expressionRunners.clear(); + return 0; +} +// ending a Binaryen API trace diff --git a/test/binaryen.js/inlining-options.js.txt b/test/binaryen.js/inlining-options.js.txt index 077e366ce..91d360449 100644 --- a/test/binaryen.js/inlining-options.js.txt +++ b/test/binaryen.js/inlining-options.js.txt @@ -10,6 +10,7 @@ int main() { std::map events; std::map exports; std::map relooperBlocks; + std::map expressionRunners; BinaryenModuleRef the_module = NULL; RelooperRef the_relooper = NULL; BinaryenGetAlwaysInlineMaxSize(); diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 128c4ba51..7497e00a9 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -10,6 +10,7 @@ int main() { std::map events; std::map exports; std::map relooperBlocks; + std::map expressionRunners; BinaryenModuleRef the_module = NULL; RelooperRef the_relooper = NULL; the_module = BinaryenModuleCreate(); @@ -5462,6 +5463,7 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7} events.clear(); exports.clear(); relooperBlocks.clear(); + expressionRunners.clear(); the_module = BinaryenModuleCreate(); expressions[size_t(NULL)] = BinaryenExpressionRef(NULL); BinaryenAddFunctionImport(the_module, "check", "module", "check", BinaryenTypeInt32(), BinaryenTypeNone()); @@ -6392,6 +6394,7 @@ optimized: events.clear(); exports.clear(); relooperBlocks.clear(); + expressionRunners.clear(); // BinaryenTypeNone: 0 // [] // BinaryenTypeUnreachable: 1 diff --git a/test/binaryen.js/low-memory-unused.js.txt b/test/binaryen.js/low-memory-unused.js.txt index 4933825a5..6f6e82d9b 100644 --- a/test/binaryen.js/low-memory-unused.js.txt +++ b/test/binaryen.js/low-memory-unused.js.txt @@ -54,6 +54,7 @@ int main() { std::map events; std::map exports; std::map relooperBlocks; + std::map expressionRunners; BinaryenModuleRef the_module = NULL; RelooperRef the_relooper = NULL; BinaryenSetLowMemoryUnused(1); diff --git a/test/binaryen.js/pass-arguments.js.txt b/test/binaryen.js/pass-arguments.js.txt index df98df08c..d13c4cad7 100644 --- a/test/binaryen.js/pass-arguments.js.txt +++ b/test/binaryen.js/pass-arguments.js.txt @@ -10,6 +10,7 @@ int main() { std::map events; std::map exports; std::map relooperBlocks; + std::map expressionRunners; BinaryenModuleRef the_module = NULL; RelooperRef the_relooper = NULL; BinaryenGetPassArgument("theKey"); -- cgit v1.2.3