[wasm][fuzzer] Fuzz multi-return functions

R=ahaas@chromium.org

Bug: v8:10408
Change-Id: Ic5cb2b915584c64656bfc5baf8c9524a7d5bf48c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2196346
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67790}
This commit is contained in:
Thibaud Michaud 2020-05-12 16:39:52 +02:00 committed by Commit Bot
parent a25dce8a05
commit 63c84da67c

View File

@ -119,7 +119,7 @@ class WasmGenerator {
BlockScope(WasmGenerator* gen, WasmOpcode block_type, ValueType result_type, BlockScope(WasmGenerator* gen, WasmOpcode block_type, ValueType result_type,
ValueType br_type) ValueType br_type)
: gen_(gen) { : gen_(gen) {
gen->blocks_.push_back(br_type); gen->blocks_.emplace_back(1, br_type);
gen->builder_->EmitWithU8(block_type, result_type.value_type_code()); gen->builder_->EmitWithU8(block_type, result_type.value_type_code());
} }
@ -165,9 +165,11 @@ class WasmGenerator {
// There is always at least the block representing the function body. // There is always at least the block representing the function body.
DCHECK(!blocks_.empty()); DCHECK(!blocks_.empty());
const uint32_t target_block = data->get<uint32_t>() % blocks_.size(); const uint32_t target_block = data->get<uint32_t>() % blocks_.size();
const ValueType break_type = blocks_[target_block]; const auto break_types = blocks_[target_block];
Generate(break_type, data); for (size_t i = 0; i < break_types.size(); ++i) {
Generate(break_types[i], data);
}
builder_->EmitWithI32V( builder_->EmitWithI32V(
kExprBr, static_cast<uint32_t>(blocks_.size()) - 1 - target_block); kExprBr, static_cast<uint32_t>(blocks_.size()) - 1 - target_block);
} }
@ -177,13 +179,19 @@ class WasmGenerator {
// There is always at least the block representing the function body. // There is always at least the block representing the function body.
DCHECK(!blocks_.empty()); DCHECK(!blocks_.empty());
const uint32_t target_block = data->get<uint32_t>() % blocks_.size(); const uint32_t target_block = data->get<uint32_t>() % blocks_.size();
const ValueType break_type = blocks_[target_block]; const auto break_types = blocks_[target_block];
for (size_t i = 0; i < break_types.size(); ++i) {
Generate(break_types[i], data);
}
Generate(break_type, data);
Generate(kWasmI32, data); Generate(kWasmI32, data);
builder_->EmitWithI32V( builder_->EmitWithI32V(
kExprBrIf, static_cast<uint32_t>(blocks_.size()) - 1 - target_block); kExprBrIf, static_cast<uint32_t>(blocks_.size()) - 1 - target_block);
ConvertOrGenerate(break_type, ValueType(wanted_type), data); for (size_t i = 0; i < break_types.size() - 1; ++i) {
builder_->Emit(kExprDrop);
}
ConvertOrGenerate(break_types.front(), ValueType(wanted_type), data);
} }
// TODO(eholk): make this function constexpr once gcc supports it // TODO(eholk): make this function constexpr once gcc supports it
@ -399,19 +407,30 @@ class WasmGenerator {
} }
// Emit call. // Emit call.
builder_->EmitWithU32V(kExprCallFunction, func_index); builder_->EmitWithU32V(kExprCallFunction, func_index);
// Convert the return value to the wanted type. if (sig->return_count() == 0 && wanted_type != kWasmStmt) {
ValueType return_type =
sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(0);
if (return_type == kWasmStmt && wanted_type != kWasmStmt) {
// The call did not generate a value. Thus just generate it here. // The call did not generate a value. Thus just generate it here.
Generate(wanted_type, data); Generate(wanted_type, data);
} else if (return_type != kWasmStmt && wanted_type == kWasmStmt) { return;
// The call did generate a value, but we did not want one.
builder_->Emit(kExprDrop);
} else if (return_type != wanted_type) {
// If the returned type does not match the wanted type, convert it.
Convert(return_type, wanted_type);
} }
if (wanted_type == kWasmStmt) {
// The call did generate values, but we did not want one.
for (size_t i = 0; i < sig->return_count(); ++i) {
builder_->Emit(kExprDrop);
}
return;
}
// Keep exactly one of the return values on the stack with a combination of
// drops and selects, then convert this value to the wanted type.
size_t return_index = data->get<uint8_t>() % sig->return_count();
for (size_t i = sig->return_count() - 1; i > return_index; --i) {
builder_->Emit(kExprDrop);
}
for (size_t i = return_index; i > 0; --i) {
Convert(sig->GetReturn(i), sig->GetReturn(i - 1));
builder_->EmitI32Const(0);
builder_->Emit(kExprSelect);
}
Convert(sig->GetReturn(0), wanted_type);
} }
struct Var { struct Var {
@ -570,8 +589,14 @@ class WasmGenerator {
globals_(globals), globals_(globals),
mutable_globals_(mutable_globals) { mutable_globals_(mutable_globals) {
FunctionSig* sig = fn->signature(); FunctionSig* sig = fn->signature();
DCHECK_GE(1, sig->return_count()); if (sig->return_count() == 0) {
blocks_.push_back(sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(0)); blocks_.emplace_back(1, kWasmStmt);
} else {
blocks_.emplace_back();
for (size_t i = 0; i < sig->return_count(); ++i) {
blocks_.back().push_back(sig->GetReturn(i));
}
}
constexpr uint32_t kMaxLocals = 32; constexpr uint32_t kMaxLocals = 32;
locals_.resize(data->get<uint8_t>() % kMaxLocals); locals_.resize(data->get<uint8_t>() % kMaxLocals);
@ -596,7 +621,7 @@ class WasmGenerator {
private: private:
WasmFunctionBuilder* builder_; WasmFunctionBuilder* builder_;
std::vector<ValueType> blocks_; std::vector<std::vector<ValueType>> blocks_;
const std::vector<FunctionSig*>& functions_; const std::vector<FunctionSig*>& functions_;
std::vector<ValueType> locals_; std::vector<ValueType> locals_;
std::vector<ValueType> globals_; std::vector<ValueType> globals_;
@ -1331,10 +1356,11 @@ FunctionSig* GenerateSig(Zone* zone, DataRange* data) {
// Generate enough parameters to spill some to the stack. // Generate enough parameters to spill some to the stack.
constexpr int kMaxParameters = 15; constexpr int kMaxParameters = 15;
int num_params = int{data->get<uint8_t>()} % (kMaxParameters + 1); int num_params = int{data->get<uint8_t>()} % (kMaxParameters + 1);
bool has_return = data->get<bool>(); constexpr int kMaxReturns = 15;
int num_returns = int{data->get<uint8_t>()} % (kMaxReturns + 1);
FunctionSig::Builder builder(zone, has_return ? 1 : 0, num_params); FunctionSig::Builder builder(zone, num_returns, num_params);
if (has_return) builder.AddReturn(GetValueType(data)); for (int i = 0; i < num_returns; ++i) builder.AddReturn(GetValueType(data));
for (int i = 0; i < num_params; ++i) builder.AddParam(GetValueType(data)); for (int i = 0; i < num_params; ++i) builder.AddParam(GetValueType(data));
return builder.Build(); return builder.Build();
} }
@ -1386,9 +1412,12 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
WasmGenerator gen(f, function_signatures, globals, mutable_globals, WasmGenerator gen(f, function_signatures, globals, mutable_globals,
&function_range); &function_range);
ValueType return_type = if (sig->return_count() == 0) {
sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(0); gen.Generate(kWasmStmt, &function_range);
gen.Generate(return_type, &function_range); }
for (size_t i = 0; i < sig->return_count(); ++i) {
gen.Generate(sig->GetReturn(i), &function_range);
}
f->Emit(kExprEnd); f->Emit(kExprEnd);
if (i == 0) builder.AddExport(CStrVector("main"), f); if (i == 0) builder.AddExport(CStrVector("main"), f);