Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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: 2 additions & 2 deletions src/shell-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
ModuleRunner* getImportInstance(Importable* import) {
auto it = linkedInstances.find(import->module);
if (it == linkedInstances.end()) {
Fatal() << "importGlobals: unknown import: " << import->module.str << "."
<< import->base.str;
Fatal() << "getImportInstance: unknown import: " << import->module.str
<< "." << import->base.str;
}
return it->second.get();
}
Expand Down
86 changes: 56 additions & 30 deletions src/tools/execution-results.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@ struct LoggingExternalInterface : public ShellExternalInterface {
ModuleRunner* instance = nullptr;

public:
LoggingExternalInterface(Loggings& loggings, Module& wasm)
: loggings(loggings), wasm(wasm) {
LoggingExternalInterface(
Loggings& loggings,
Module& wasm,
std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances_ = {})
: ShellExternalInterface(linkedInstances_), loggings(loggings), wasm(wasm) {
for (auto& exp : wasm.exports) {
if (exp->kind == ExternalKind::Table && exp->name == "table") {
exportedTable = *exp->getInternalName();
Expand Down Expand Up @@ -185,7 +188,11 @@ struct LoggingExternalInterface : public ShellExternalInterface {
} else if (import->base == "getTempRet0") {
return {Literal(state.tempRet0)};
}
} else if (linkedInstances.count(import->module)) {
// This is from a recognized module.
return getImportInstance(import)->callExport(import->base, arguments);
}
// Anything else, we ignore.
std::cerr << "[LoggingExternalInterface ignoring an unknown import "
<< import->module << " . " << import->base << '\n';
return {};
Expand Down Expand Up @@ -279,35 +286,24 @@ struct ExecutionResults {
// If set, we should ignore this and not compare it to anything.
bool ignore = false;

// get results of execution
void get(Module& wasm) {
LoggingExternalInterface interface(loggings, wasm);
// Get results of executing a module. Optionally, provide a second module to
// link with it (like fuzz_shell's second module).
void get(Module& wasm, Module* second = nullptr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of weird to have a void return on a function called get, but I guess that's not directly related to this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah, maybe this could be renamed to "gather" or "collect".

try {
ModuleRunner instance(wasm, &interface);
// This is not an optimization: we want to execute anything, even relaxed
// SIMD instructions.
instance.setRelaxedBehavior(ModuleRunner::RelaxedBehavior::Execute);
instance.instantiate();
interface.setModuleRunner(&instance);
// execute all exported methods (that are therefore preserved through
// opts)
for (auto& exp : wasm.exports) {
if (exp->kind != ExternalKind::Function) {
continue;
}
std::cout << "[fuzz-exec] calling " << exp->name << "\n";
auto* func = wasm.getFunction(*exp->getInternalName());
FunctionResult ret = run(func, wasm, instance);
results[exp->name] = ret;
if (auto* values = std::get_if<Literals>(&ret)) {
// ignore the result if we hit an unreachable and returned no value
if (values->size() > 0) {
std::cout << "[fuzz-exec] note result: " << exp->name << " => ";
for (auto value : *values) {
printValue(value);
}
}
}
// Run the first module.
LoggingExternalInterface interface(loggings, wasm);
auto instance = std::make_shared<ModuleRunner>(wasm, &interface);
runModule(wasm, *instance, interface);

if (second) {
// Link and run the second module.
std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances;
linkedInstances["primary"] = instance;
LoggingExternalInterface secondInterface(
loggings, *second, linkedInstances);
auto secondInstance = std::make_shared<ModuleRunner>(
*second, &secondInterface, linkedInstances);
runModule(*second, *secondInstance, secondInterface);
}
} catch (const TrapException&) {
// May throw in instance creation (init of offsets).
Expand All @@ -319,6 +315,36 @@ struct ExecutionResults {
}
}

void runModule(Module& wasm,
ModuleRunner& instance,
LoggingExternalInterface& interface) {
// This is not an optimization: we want to execute anything, even relaxed
// SIMD instructions.
instance.setRelaxedBehavior(ModuleRunner::RelaxedBehavior::Execute);
instance.instantiate();
interface.setModuleRunner(&instance);
// execute all exported methods (that are therefore preserved through
// opts)
for (auto& exp : wasm.exports) {
if (exp->kind != ExternalKind::Function) {
continue;
}
std::cout << "[fuzz-exec] calling " << exp->name << "\n";
auto* func = wasm.getFunction(*exp->getInternalName());
FunctionResult ret = run(func, wasm, instance);
results[exp->name] = ret;
if (auto* values = std::get_if<Literals>(&ret)) {
// ignore the result if we hit an unreachable and returned no value
if (values->size() > 0) {
std::cout << "[fuzz-exec] note result: " << exp->name << " => ";
for (auto value : *values) {
printValue(value);
}
}
}
}
}

void printValue(Literal value) {
// Unwrap an externalized GC value to get the actual value, but not strings,
// which are normally a subtype of ext.
Expand Down
21 changes: 20 additions & 1 deletion src/tools/wasm-opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ int main(int argc, const char* argv[]) {
bool converge = false;
bool fuzzExecBefore = false;
bool fuzzExecAfter = false;
std::string fuzzExecSecond;
std::string extraFuzzCommand;
bool translateToFuzz = false;
std::string initialFuzz;
Expand Down Expand Up @@ -148,6 +149,15 @@ For more on how to optimize effectively, see
[&](Options* o, const std::string& arguments) {
fuzzExecBefore = fuzzExecAfter = true;
})
.add("--fuzz-exec-second",
"",
"A second module to link with the first, for fuzz-exec-before (only "
"before, as optimizations are not applied to it)",
WasmOptOption,
Options::Arguments::One,
[&](Options* o, const std::string& arguments) {
fuzzExecSecond = arguments;
})
.add("--extra-fuzz-command",
"-efc",
"An extra command to run on the output before and after optimizing. "
Expand Down Expand Up @@ -345,7 +355,16 @@ For more on how to optimize effectively, see

ExecutionResults results;
if (fuzzExecBefore) {
results.get(wasm);
if (fuzzExecSecond.empty()) {
results.get(wasm);
} else {
// Add the second module.
Module second;
second.features = wasm.features;
ModuleReader().read(fuzzExecSecond, second);

results.get(wasm, &second);
}
}

if (emitSpecWrapper.size() > 0) {
Expand Down
17 changes: 17 additions & 0 deletions test/lit/exec/second.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

;; RUN: wasm-opt %s -all --fuzz-exec-before --fuzz-exec-second=%s.second.wast -q -o /dev/null 2>&1 | filecheck %s

(module
(func $first (export "first") (result i32)
(i32.const 42)
)
)

;; Test that --fuzz-exec-second will run a second wasm file that is
;; provided, and call its exports as well as the first module's.

;; CHECK: [fuzz-exec] calling first
;; CHECK-NEXT: [fuzz-exec] note result: first => 42
;; CHECK: [fuzz-exec] calling second
;; CHECK-NEXT: [fuzz-exec] note result: second => 1337

12 changes: 12 additions & 0 deletions test/lit/exec/second.wast.second.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(module
(import "primary" "first" (func $first-func (result i32)))

(func $second (export "second") (result i32)
;; Test we can call the first module, linked as "primary."
(i32.add
(call $first-func)
(i32.const 1295)
)
)
)

5 changes: 5 additions & 0 deletions test/lit/help/wasm-opt.test
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
;; CHECK-NEXT: after optimization, helping
;; CHECK-NEXT: fuzzing find bugs
;; CHECK-NEXT:
;; CHECK-NEXT: --fuzz-exec-second A second module to link with the
;; CHECK-NEXT: first, for fuzz-exec-before
;; CHECK-NEXT: (only before, as optimizations
;; CHECK-NEXT: are not applied to it)
;; CHECK-NEXT:
;; CHECK-NEXT: --extra-fuzz-command,-efc An extra command to run on the
;; CHECK-NEXT: output before and after
;; CHECK-NEXT: optimizing. The output is
Expand Down
Loading