diff options
Diffstat (limited to 'scripts/benchmarking/bench.wat')
-rw-r--r-- | scripts/benchmarking/bench.wat | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/scripts/benchmarking/bench.wat b/scripts/benchmarking/bench.wat new file mode 100644 index 000000000..87d54b66b --- /dev/null +++ b/scripts/benchmarking/bench.wat @@ -0,0 +1,283 @@ +;; See bench.js. + +(module + ;; A chain of three types. Each type has a "next" field, so we can form linked + ;; lists. + (type $A (sub (struct (field $next (ref null $A))))) + (type $B (sub $A (struct (field $next (ref null $A))))) + (type $C (sub $B (struct (field $next (ref null $A))))) + + (type $func (func (param (ref $A)) (result i32))) + + ;; Internal helper to iterate over a linked list and call a function on each + ;; item, and return the sum of those calls' results. + (func $iter (param $list (ref null $A)) (param $func (ref $func)) (result i32) + (local $sum i32) + (loop $loop + (if + (ref.is_null + (local.get $list) + ) + (then + (return + (local.get $sum) + ) + ) + (else + (local.set $sum + (i32.add + (local.get $sum) + (call_ref $func + (ref.as_non_null + (local.get $list) + ) + (local.get $func) + ) + ) + ) + (local.set $list + (struct.get $A $next + (local.get $list) + ) + ) + (br $loop) + ) + ) + ) + ) + + ;; Using the helper, and depending on inlining to optimize this, lets us + ;; write the exports concisely. First, code to compute the length of the list + ;; (for comparison purposes). + (func $len (export "len") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + ;; Add one each time this is called. + (ref.func $one) + ) + ) + (func $one (param $list (ref $A)) (result i32) + (i32.const 1) + ) + + ;; At each point in the linked list, check if both the current and next item + ;; are inputs are $B, using an if to short-circuit when possible. + (func $iff-both (export "iff-both") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-iff-both) + ) + ) + (func $do-iff-both (param $list (ref $A)) (result i32) + (if (result i32) + (ref.test (ref $B) + (struct.get $A $next + (local.get $list) + ) + ) + (then + (ref.test (ref $B) + (local.get $list) + ) + ) + (else + (i32.const 0) + ) + ) + ) + + ;; The same computation, but using an and, so both tests always execute. + (func $and (export "and") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-and) + ) + ) + (func $do-and (param $list (ref $A)) (result i32) + (i32.and + (ref.test (ref $B) + (struct.get $A $next + (local.get $list) + ) + ) + (ref.test (ref $B) + (local.get $list) + ) + ) + ) + + ;; Similar, but return 1 if either test succeeds (using an if). + (func $iff-either (export "iff-either") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-iff-either) + ) + ) + (func $do-iff-either (param $list (ref $A)) (result i32) + (if (result i32) + (ref.test (ref $B) + (struct.get $A $next + (local.get $list) + ) + ) + (then + (i32.const 1) + ) + (else + (ref.test (ref $B) + (local.get $list) + ) + ) + ) + ) + + + ;; The same computation, but using an or, so both tests always execute. + (func $or (export "or") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-or) + ) + ) + (func $do-or (param $list (ref $A)) (result i32) + (i32.or + (ref.test (ref $B) + (struct.get $A $next + (local.get $list) + ) + ) + (ref.test (ref $B) + (local.get $list) + ) + ) + ) + + ;; Use a select to do a test of "is next null ? 0 : test curr". + (func $select (export "select") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-select) + ) + ) + (func $do-select (param $list (ref $A)) (result i32) + (select + (i32.const 0) + (ref.test (ref $B) + (local.get $list) + ) + (ref.is_null + (struct.get $A $next + (local.get $list) + ) + ) + ) + ) + + ;; Use an iff to do the same. + (func $iff-nextor (export "iff-nextor") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-iff-nextor) + ) + ) + (func $do-iff-nextor (param $list (ref $A)) (result i32) + (if (result i32) + (ref.is_null + (struct.get $A $next + (local.get $list) + ) + ) + (then + (i32.const 0) + ) + (else + (ref.test (ref $B) + (local.get $list) + ) + ) + ) + ) + + ;; Use an if over three tests: "test if next is B or C depending on if curr is + ;; B." + (func $iff-three (export "iff-three") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-iff-three) + ) + ) + (func $do-iff-three (param $list (ref $A)) (result i32) + (local $next (ref null $A)) + (local.set $next + (struct.get $A $next + (local.get $list) + ) + ) + (if (result i32) + (ref.test (ref $B) + (local.get $list) + ) + (then + (ref.test (ref $C) + (local.get $next) + ) + ) + (else + (ref.test (ref $B) + (local.get $next) + ) + ) + ) + ) + + ;; Use a select for the same. + (func $select-three (export "select-three") (param $list (ref $A)) (result i32) + (call $iter + (local.get $list) + (ref.func $do-select-three) + ) + ) + (func $do-select-three (param $list (ref $A)) (result i32) + (local $next (ref null $A)) + (local.set $next + (struct.get $A $next + (local.get $list) + ) + ) + (select + (ref.test (ref $C) + (local.get $next) + ) + (ref.test (ref $B) + (local.get $next) + ) + (ref.test (ref $B) + (local.get $list) + ) + ) + ) + + ;; Creation functions. + + (func $makeA (export "makeA") (param $next (ref null $A)) (result anyref) + (struct.new $A + (local.get $next) + ) + ) + + (func $makeB (export "makeB") (param $next (ref null $A)) (result anyref) + (struct.new $B + (local.get $next) + ) + ) + + (func $makeC (export "makeC") (param $next (ref null $A)) (result anyref) + ;; This function is not used in benchmarks yet, but it keeps the type $C + ;; alive, which prevents $B from looking like it could be final, which might + ;; allow the optimizer to simplify more than we want. + (struct.new $C + (local.get $next) + ) + ) +) + |