| #include "ir/utils.h" |
| #include "passes/stringify-walker.h" |
| #include "print-test.h" |
| #include "support/suffix_tree.h" |
| |
| using namespace wasm; |
| |
| using StringifyTest = PrintTest; |
| |
| TEST_F(StringifyTest, Print) { |
| auto moduleText = R"wasm( |
| (module |
| (tag $catch_a (param i32)) |
| (tag $catch_b (param i32)) |
| (tag $catch_c (param i32)) |
| (func $d |
| (block $block_a |
| (drop (i32.const 20)) |
| (drop (i32.const 10)) |
| ) |
| (block $block_b |
| (drop (if (result i32) |
| (i32.const 0) |
| (then (i32.const 40)) |
| (else (i32.const 5)) |
| )) |
| ) |
| (block $block_c |
| (drop (if (result i32) |
| (i32.const 1) |
| (then (i32.const 30)) |
| )) |
| ) |
| (block $block_d |
| (try $try_a |
| (do |
| (nop) |
| ) |
| (catch $catch_a |
| (drop (i32.const 8)) |
| ) |
| (catch $catch_b |
| (drop (i32.const 15)) |
| ) |
| ) |
| ) |
| (block $block_e |
| (try $try_b |
| (do |
| (nop) |
| ) |
| (catch $catch_c |
| (drop (i32.const 33)) |
| ) |
| ) |
| ) |
| ) |
| ) |
| )wasm"; |
| |
| auto stringifyText = R"stringify(adding unique symbol for Func Start |
| in visitExpression for block |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for block $block_a |
| in visitExpression for block $block_b |
| in visitExpression for block $block_c |
| in visitExpression for block $block_d |
| in visitExpression for block $block_e |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for i32.const 20 |
| in visitExpression for drop |
| in visitExpression for i32.const 10 |
| in visitExpression for drop |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for i32.const 0 |
| in visitExpression for if (result i32) |
| in visitExpression for drop |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for i32.const 1 |
| in visitExpression for if (result i32) |
| in visitExpression for drop |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for try $try_a |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for try $try_b |
| adding unique symbol for End |
| adding unique symbol for If Start |
| in visitExpression for i32.const 40 |
| adding unique symbol for Else Start |
| in visitExpression for i32.const 5 |
| adding unique symbol for End |
| adding unique symbol for If Start |
| in visitExpression for i32.const 30 |
| adding unique symbol for End |
| adding unique symbol for Try Body Start |
| in visitExpression for nop |
| adding unique symbol for End |
| adding unique symbol for Try Catch Start |
| in visitExpression for block |
| adding unique symbol for End |
| adding unique symbol for Try Catch Start |
| in visitExpression for block |
| adding unique symbol for End |
| adding unique symbol for Try Body Start |
| in visitExpression for nop |
| adding unique symbol for End |
| adding unique symbol for Try Catch Start |
| in visitExpression for block |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for pop i32 |
| in visitExpression for i32.const 8 |
| in visitExpression for drop |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for pop i32 |
| in visitExpression for i32.const 15 |
| in visitExpression for drop |
| adding unique symbol for End |
| adding unique symbol for Block Start |
| in visitExpression for pop i32 |
| in visitExpression for i32.const 33 |
| in visitExpression for drop |
| adding unique symbol for End |
| )stringify"; |
| |
| struct TestStringifyWalker : public StringifyWalker<TestStringifyWalker> { |
| std::ostream& os; |
| |
| TestStringifyWalker(std::ostream& os) : os(os){}; |
| |
| void addUniqueSymbol(SeparatorReason reason) { |
| os << "adding unique symbol for " << reason << "\n"; |
| } |
| |
| void visitExpression(Expression* curr) { |
| os << "in visitExpression for " << ShallowExpression{curr, getModule()} |
| << std::endl; |
| } |
| }; |
| |
| Module wasm; |
| parseWast(wasm, moduleText); |
| |
| std::stringstream ss; |
| TestStringifyWalker stringify = TestStringifyWalker(ss); |
| stringify.walkModule(&wasm); |
| |
| EXPECT_EQ(ss.str(), stringifyText); |
| } |
| |
| static auto dupModuleText = R"wasm( |
| (module |
| (func $a |
| (block $block_a |
| (drop (i32.const 20)) |
| (drop (i32.const 10)) |
| ) |
| (block $block_b |
| (drop (if (result i32) |
| (i32.const 0) |
| (then (i32.const 40)) |
| (else (i32.const 5)) |
| )) |
| ) |
| (block $block_c |
| (drop (if (result i32) |
| (i32.const 1) |
| (then (i32.const 30)) |
| )) |
| ) |
| (block $block_d |
| (drop (i32.const 20)) |
| (drop (i32.const 10)) |
| ) |
| (block $block_e |
| (drop (if (result i32) |
| (i32.const 1) |
| (then (i32.const 30)) |
| )) |
| ) |
| (block $block_f |
| (drop (if (result i32) |
| (i32.const 0) |
| (then (i32.const 30)) |
| )) |
| ) |
| ) |
| ) |
| )wasm"; |
| |
| std::vector<uint32_t> hashStringifyModule(Module* wasm) { |
| HashStringifyWalker stringify = HashStringifyWalker(); |
| stringify.walkModule(wasm); |
| return stringify.hashString; |
| } |
| |
| TEST_F(StringifyTest, Stringify) { |
| Module wasm; |
| parseWast(wasm, dupModuleText); |
| auto hashString = hashStringifyModule(&wasm); |
| |
| EXPECT_EQ(hashString, |
| (std::vector<uint32_t>{ |
| (uint32_t)-1, // function start |
| 0, // function block evaluated as a whole |
| (uint32_t)-2, // end |
| (uint32_t)-3, // block start |
| 1, // block_a evaluated as a whole |
| 2, // block_b evaluated as a whole |
| 3, // block_c evaluated as a whole |
| 1, // block_d has the same contents as block_a |
| 3, // block_e has the same contents as block_c |
| 4, // block_f evaluated as a whole |
| (uint32_t)-4, // end |
| (uint32_t)-5, // block start for block_a |
| 5, // i32.const 20 |
| 6, // drop, all drops will be the same symbol |
| 7, // i32.const 10 |
| 6, // drop |
| (uint32_t)-6, // end |
| (uint32_t)-7, // block start for block_b |
| 8, // i32.const 0, if condition |
| 9, // block_b's if evaluated as a whole |
| 6, // drop |
| (uint32_t)-8, // end |
| (uint32_t)-9, // block start for block_c |
| 10, // i32.const 1, if condition |
| 11, // block_c's if evaluated as a whole |
| 6, // drop |
| (uint32_t)-10, // end |
| (uint32_t)-11, // block start for block_d |
| 5, // i32.const 20 |
| 6, // drop |
| 7, // i32.const 10 |
| 6, // drop |
| (uint32_t)-12, // end |
| (uint32_t)-13, // block start for block_e |
| 10, // i32.const 1, if condition |
| 11, // block_e if evaluated as a whole |
| 6, // drop |
| (uint32_t)-14, // end |
| (uint32_t)-15, // block start for block_f |
| 8, // i32.const 0, if condition |
| 11, // block_f's if evaluated as a whole |
| 6, // drop |
| (uint32_t)-16, // end |
| (uint32_t)-17, // if start in block_b |
| 12, // i32.const 40 |
| (uint32_t)-18, // else start in block_b |
| 13, // i32.const 5 |
| (uint32_t)-19, // end |
| (uint32_t)-20, // if start in block_c |
| 14, // i32.const 30 |
| (uint32_t)-21, // end |
| (uint32_t)-22, // if start in block_e |
| 14, // i32.const 30 |
| (uint32_t)-23, // end |
| (uint32_t)-24, // if start in block_f |
| 14, // i32.const 30 |
| (uint32_t)-25, // end |
| })); |
| } |
| |
| TEST_F(StringifyTest, Substrings) { |
| Module wasm; |
| parseWast(wasm, dupModuleText); |
| auto hashString = hashStringifyModule(&wasm); |
| auto substrings = StringifyProcessor::repeatSubstrings(hashString); |
| |
| EXPECT_EQ( |
| substrings, |
| (std::vector<SuffixTree::RepeatedSubstring>{ |
| // 5, 6, 7, 6 appears at idx 9 and again at 22 |
| SuffixTree::RepeatedSubstring{4u, (std::vector<unsigned>{12, 28})}, |
| // 6, 7, 6 appears at idx 10 and again at 23 |
| SuffixTree::RepeatedSubstring{3u, (std::vector<unsigned>{13, 29})}, |
| // 10, 11, 6 appears at idx 18 and again at 27 |
| SuffixTree::RepeatedSubstring{3u, (std::vector<unsigned>{23, 34})}, |
| // 11, 6 appears at idx 32, 19 and again at 28 |
| SuffixTree::RepeatedSubstring{2u, (std::vector<unsigned>{24, 35, 40})}, |
| // 7, 6 appears at idx 11 and again at 24 |
| SuffixTree::RepeatedSubstring{2u, (std::vector<unsigned>{14, 30})}})); |
| } |
| |
| TEST_F(StringifyTest, DedupeSubstrings) { |
| Module wasm; |
| parseWast(wasm, dupModuleText); |
| auto hashString = hashStringifyModule(&wasm); |
| std::vector<SuffixTree::RepeatedSubstring> substrings = |
| StringifyProcessor::repeatSubstrings(hashString); |
| auto result = StringifyProcessor::dedupe(substrings); |
| |
| EXPECT_EQ( |
| result, |
| (std::vector<SuffixTree::RepeatedSubstring>{ |
| // 5, 6, 7, 6 appears at idx 12 and again at 28 |
| SuffixTree::RepeatedSubstring{4u, (std::vector<unsigned>{12, 28})}, |
| // 10, 11, 6 appears at idx 23 and again at 34 |
| SuffixTree::RepeatedSubstring{3u, (std::vector<unsigned>{23, 34})}})); |
| } |
| |
| TEST_F(StringifyTest, FilterLocalSets) { |
| static auto localSetModuleText = R"wasm( |
| (module |
| (func $a (result i32) |
| (local $x i32) |
| (local.set $x |
| (i32.const 1) |
| ) |
| (i32.const 0) |
| (i32.const 1) |
| ) |
| (func $b (result i32) |
| (local $x i32) |
| (local.set $x |
| (i32.const 1) |
| ) |
| (i32.const 5) |
| (i32.const 0) |
| (i32.const 1) |
| ) |
| ) |
| )wasm"; |
| Module wasm; |
| parseWast(wasm, localSetModuleText); |
| HashStringifyWalker stringify = HashStringifyWalker(); |
| stringify.walkModule(&wasm); |
| auto substrings = StringifyProcessor::repeatSubstrings(stringify.hashString); |
| auto result = |
| StringifyProcessor::filterLocalSets(substrings, stringify.exprs); |
| |
| EXPECT_EQ( |
| result, |
| (std::vector<SuffixTree::RepeatedSubstring>{ |
| // sequence i32.const 0, i32.const 1 appears at idx 6 and again at 16 |
| SuffixTree::RepeatedSubstring{2u, (std::vector<unsigned>{6, 16})}})); |
| } |
| |
| // TODO: Switching to the new parser broke this test. Fix it. |
| |
| // TEST_F(StringifyTest, FilterBranches) { |
| // static auto branchesModuleText = R"wasm( |
| // (module |
| // (func $a (result i32) |
| // (block $top (result i32) |
| // (br $top) |
| // ) |
| // (i32.const 7) |
| // (i32.const 1) |
| // (i32.const 2) |
| // (i32.const 4) |
| // (i32.const 3) |
| // (return) |
| // ) |
| // (func $b (result i32) |
| // (block $top (result i32) |
| // (br $top) |
| // ) |
| // (i32.const 0) |
| // (i32.const 1) |
| // (i32.const 2) |
| // (i32.const 5) |
| // (i32.const 3) |
| // (return) |
| // ) |
| // ) |
| // )wasm"; |
| // Module wasm; |
| // parseWast(wasm, branchesModuleText); |
| // HashStringifyWalker stringify = HashStringifyWalker(); |
| // stringify.walkModule(&wasm); |
| // auto substrings = |
| // StringifyProcessor::repeatSubstrings(stringify.hashString); |
| // auto result = |
| // StringifyProcessor::filterBranches(substrings, stringify.exprs); |
| |
| // EXPECT_EQ( |
| // result, |
| // (std::vector<SuffixTree::RepeatedSubstring>{ |
| // // sequence i32.const 1, i32.const 2 is at idx 6 and 21 |
| // SuffixTree::RepeatedSubstring{2u, (std::vector<unsigned>{6, 21})}})); |
| // } |