Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzer: Match the logging of i31ref between JS and C++ #6335

Merged
merged 4 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
56 changes: 35 additions & 21 deletions src/tools/execution-results.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand All @@ -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;
Expand Down
29 changes: 29 additions & 0 deletions test/lit/exec/i31.wast
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Loading