From ed08cc80879d3b3b5cd35fc8013c68c9f2b24466 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 21 Feb 2024 16:43:23 -0800 Subject: [PATCH 1/4] start --- src/tools/execution-results.h | 54 +++++++++++++++++++++-------------- test/lit/exec/i31.wast | 17 +++++++++++ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index add7cd3a5a3..852472b451a 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -115,27 +115,8 @@ struct ExecutionResults { // ignore the result if we hit an unreachable and returned no value if (values->size() > 0) { std::cout << "[fuzz-exec] note result: " << exp->name << " => "; - auto resultType = func->getResults(); - if (resultType.isRef() && !resultType.isString()) { - // Don't print reference values, as funcref(N) contains an index - // for example, which is not guaranteed to remain identical after - // optimizations. Do not print the type in detail (as even that - // may change due to closed-world optimizations); just print a - // simple type like JS does, 'object' or 'function', but also - // print null for a null (so a null function does not get - // printed as object, as in JS we have typeof null == 'object'). - if (values->size() == 1 && (*values)[0].isNull()) { - std::cout << "null\n"; - } else if (resultType.isFunction()) { - std::cout << "function\n"; - } else { - std::cout << "object\n"; - } - } else { - // Non-references can be printed in full. So can strings, since we - // always know how to print them and there is just one string - // type. - std::cout << *values << '\n'; + for (auto value : *values) { + printValue(value); } } } @@ -150,6 +131,37 @@ struct ExecutionResults { } } + void printValue(Literal value) { + // Unwrap an externalized value to get the actual value. + if (Type::isSubType(value.type, Type(HeapType::ext, Nullable))) { + value = value.internalize(); + } + + auto type = value.type; + if (type.isRef() && !type.isString()) { + // Don't print reference values, as funcref(N) contains an index + // for example, which is not guaranteed to remain identical after + // optimizations. Do not print the type in detail (as even that + // may change due to closed-world optimizations); just print a + // simple type like JS does, 'object' or 'function', but also + // print null for a null (so a null function does not get + // printed as object, as in JS we have typeof null == 'object'). + if (value.isNull()) { + std::cout << "null\n"; + } else if (type.isFunction()) { + std::cout << "function\n"; + } else { + std::cout << "object\n"; + } + return; + } + + // Non-references can be printed in full. So can strings, since we + // always know how to print them and there is just one string + // type. + std::cout << value << '\n'; + } + // get current results and check them against previous ones void check(Module& wasm) { ExecutionResults optimizedResults; diff --git a/test/lit/exec/i31.wast b/test/lit/exec/i31.wast index 70d220d8c50..a6fb6a182b7 100644 --- a/test/lit/exec/i31.wast +++ b/test/lit/exec/i31.wast @@ -76,6 +76,23 @@ (ref.null i31) ) ) + + (func $return-i31 (result i31ref) + ;; An i31 should be logged out using its integer value, unlike a struct or + ;; array which ends up as only "object". + (ref.i31 + (i32.const 42) + ) + ) + + (func $return-exted-i31 (result i31ref) + ;; Even an externalized i31 is logged out using its integer value. + (extern.externalize + (ref.i31 + (i32.const 42) + ) + ) + ) ) ;; CHECK: [fuzz-exec] calling null-local ;; CHECK-NEXT: [fuzz-exec] note result: null-local => 1 From 50bbdbb1452592f7d2576728a9e980cb20ef9900 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 21 Feb 2024 16:51:03 -0800 Subject: [PATCH 2/4] print --- src/tools/execution-results.h | 26 ++++++++++++++------------ test/lit/exec/i31.wast | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 852472b451a..40d844f19d0 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -137,16 +137,20 @@ struct ExecutionResults { value = value.internalize(); } + // Don't print most reference values, as e.g. funcref(N) contains an index, + // which is not guaranteed to remain identical after optimizations. Do not + // print the type in detail (as even that may change due to closed-world + // optimizations); just print a simple type like JS does, 'object' or + // 'function', but also print null for a null (so a null function does not + // get printed as object, as in JS we have typeof null == 'object'). + // + // The only references we print in full are strings and i31s, which have + // simple and stable internal structures that optimizations will not alter. auto type = value.type; - if (type.isRef() && !type.isString()) { - // Don't print reference values, as funcref(N) contains an index - // for example, which is not guaranteed to remain identical after - // optimizations. Do not print the type in detail (as even that - // may change due to closed-world optimizations); just print a - // simple type like JS does, 'object' or 'function', but also - // print null for a null (so a null function does not get - // printed as object, as in JS we have typeof null == 'object'). - if (value.isNull()) { + if (type.isRef()) { + if (type.isString() || type.getHeapType() == HeapType::i31) { + std::cout << value << '\n'; + } else if (value.isNull()) { std::cout << "null\n"; } else if (type.isFunction()) { std::cout << "function\n"; @@ -156,9 +160,7 @@ struct ExecutionResults { return; } - // Non-references can be printed in full. So can strings, since we - // always know how to print them and there is just one string - // type. + // Non-references can be printed in full. std::cout << value << '\n'; } diff --git a/test/lit/exec/i31.wast b/test/lit/exec/i31.wast index a6fb6a182b7..0833d490ddb 100644 --- a/test/lit/exec/i31.wast +++ b/test/lit/exec/i31.wast @@ -77,7 +77,9 @@ ) ) - (func $return-i31 (result i31ref) + ;; CHECK: [fuzz-exec] calling return-i31 + ;; CHECK-NEXT: [fuzz-exec] note result: return-i31 => i31ref(42) + (func $return-i31 (export "return-i31") (result i31ref) ;; An i31 should be logged out using its integer value, unlike a struct or ;; array which ends up as only "object". (ref.i31 @@ -85,7 +87,9 @@ ) ) - (func $return-exted-i31 (result i31ref) + ;; CHECK: [fuzz-exec] calling return-exted-i31 + ;; CHECK-NEXT: [fuzz-exec] note result: return-exted-i31 => i31ref(42) + (func $return-exted-i31 (export "return-exted-i31") (result externref) ;; Even an externalized i31 is logged out using its integer value. (extern.externalize (ref.i31 @@ -114,10 +118,18 @@ ;; CHECK: [fuzz-exec] calling trap ;; CHECK-NEXT: [trap null ref] + +;; CHECK: [fuzz-exec] calling return-i31 +;; CHECK-NEXT: [fuzz-exec] note result: return-i31 => i31ref(42) + +;; CHECK: [fuzz-exec] calling return-exted-i31 +;; CHECK-NEXT: [fuzz-exec] note result: return-exted-i31 => i31ref(42) ;; CHECK-NEXT: [fuzz-exec] comparing nn-s ;; CHECK-NEXT: [fuzz-exec] comparing nn-u ;; CHECK-NEXT: [fuzz-exec] comparing non-null ;; CHECK-NEXT: [fuzz-exec] comparing null-immediate ;; CHECK-NEXT: [fuzz-exec] comparing null-local +;; CHECK-NEXT: [fuzz-exec] comparing return-exted-i31 +;; CHECK-NEXT: [fuzz-exec] comparing return-i31 ;; CHECK-NEXT: [fuzz-exec] comparing trap ;; CHECK-NEXT: [fuzz-exec] comparing zero-is-not-null From 372dab638cb27f706edec286438a44e47134ae2f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 21 Feb 2024 17:00:20 -0800 Subject: [PATCH 3/4] fix --- scripts/fuzz_opt.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index c5498822e7c..9e6389c3a59 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -561,6 +561,10 @@ def fix_double(x): # change that index, so ignore it out = re.sub(r'funcref\([\d\w$+-_:]+\)', 'funcref()', out) + # JS prints i31 as just a number, so change "funcref(N)" (which C++ emits) + # to "N". + out = re.sub(r'i31ref\((\d+)\)', r'\1', out) + lines = out.splitlines() for i in range(len(lines)): line = lines[i] From 5b24918eb867b5de04f62b206ef37913552085e4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 22 Feb 2024 09:24:53 -0800 Subject: [PATCH 4/4] typo --- scripts/fuzz_opt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 9e6389c3a59..d0705693fd6 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -561,7 +561,7 @@ def fix_double(x): # change that index, so ignore it out = re.sub(r'funcref\([\d\w$+-_:]+\)', 'funcref()', out) - # JS prints i31 as just a number, so change "funcref(N)" (which C++ emits) + # JS prints i31 as just a number, so change "i31ref(N)" (which C++ emits) # to "N". out = re.sub(r'i31ref\((\d+)\)', r'\1', out)