Skip to content
Open
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: 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.

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