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/expressionrunner.js | 208 +++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 test/binaryen.js/expressionrunner.js (limited to 'test/binaryen.js/expressionrunner.js') 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); -- cgit v1.2.3