diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index c5498822e7c..d0705693fd6 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 "i31ref(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] diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index add7cd3a5a3..40d844f19d0 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,39 @@ 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(); + } + + // 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()) { + 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"; + } else { + std::cout << "object\n"; + } + return; + } + + // Non-references can be printed in full. + 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..0833d490ddb 100644 --- a/test/lit/exec/i31.wast +++ b/test/lit/exec/i31.wast @@ -76,6 +76,27 @@ (ref.null i31) ) ) + + ;; 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 + (i32.const 42) + ) + ) + + ;; 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 + (i32.const 42) + ) + ) + ) ) ;; CHECK: [fuzz-exec] calling null-local ;; CHECK-NEXT: [fuzz-exec] note result: null-local => 1 @@ -97,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