Commit 58c82bda authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] [fuzzer] Add support for calls

The wasm compile fuzzer now generates up to four functions with
different signatures, and generates calls between them.

R=ahaas@chromium.org
CC=eholk@chromium.org

Change-Id: I94903a80c78f8463dc1dee91ccf3be33c431e25a
Reviewed-on: https://chromium-review.googlesource.com/839860
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50274}
parent 5da204c8
...@@ -30,6 +30,8 @@ namespace fuzzer { ...@@ -30,6 +30,8 @@ namespace fuzzer {
namespace { namespace {
constexpr int kMaxFunctions = 4;
class DataRange { class DataRange {
const uint8_t* data_; const uint8_t* data_;
size_t size_; size_t size_;
...@@ -65,21 +67,17 @@ class DataRange { ...@@ -65,21 +67,17 @@ class DataRange {
template <typename T> template <typename T>
T get() { T get() {
if (size() == 0) { // We want to support the case where we have less than sizeof(T) bytes
return T(); // remaining in the slice. For example, if we emit an i32 constant, it's
} else { // okay if we don't have a full four bytes available, we'll just use what
// We want to support the case where we have less than sizeof(T) bytes // we have. We aren't concerned about endianness because we are generating
// remaining in the slice. For example, if we emit an i32 constant, it's // arbitrary expressions.
// okay if we don't have a full four bytes available, we'll just use what const size_t num_bytes = std::min(sizeof(T), size_);
// we have. We aren't concerned about endianness because we are generating T result = T();
// arbitrary expressions. memcpy(&result, data_, num_bytes);
const size_t num_bytes = std::min(sizeof(T), size()); data_ += num_bytes;
T result = T(); size_ -= num_bytes;
memcpy(&result, data_, num_bytes); return result;
data_ += num_bytes;
size_ -= num_bytes;
return result;
}
} }
DISALLOW_COPY_AND_ASSIGN(DataRange); DISALLOW_COPY_AND_ASSIGN(DataRange);
...@@ -203,6 +201,63 @@ class WasmGenerator { ...@@ -203,6 +201,63 @@ class WasmGenerator {
builder_->Emit(kExprDrop); builder_->Emit(kExprDrop);
} }
template <ValueType wanted_type>
void call(DataRange& data) {
call(data, wanted_type);
}
void Convert(ValueType src, ValueType dst) {
auto idx = [](ValueType t) -> int {
switch (t) {
case kWasmI32:
return 0;
case kWasmI64:
return 1;
case kWasmF32:
return 2;
case kWasmF64:
return 3;
default:
UNREACHABLE();
}
};
static constexpr WasmOpcode kConvertOpcodes[] = {
// {i32, i64, f32, f64} -> i32
kExprNop, kExprI32ConvertI64, kExprI32SConvertF32, kExprI32SConvertF64,
// {i32, i64, f32, f64} -> i64
kExprI64SConvertI32, kExprNop, kExprI64SConvertF32, kExprI64SConvertF64,
// {i32, i64, f32, f64} -> f32
kExprF32SConvertI32, kExprF32SConvertI64, kExprNop, kExprF32ConvertF64,
// {i32, i64, f32, f64} -> f64
kExprF64SConvertI32, kExprF64SConvertI64, kExprF64ConvertF32, kExprNop};
int arr_idx = idx(dst) << 2 | idx(src);
builder_->Emit(kConvertOpcodes[arr_idx]);
}
void call(DataRange& data, ValueType wanted_type) {
int func_index = data.get<uint8_t>() % functions_.size();
FunctionSig* sig = functions_[func_index];
// Generate arguments.
for (size_t i = 0; i < sig->parameter_count(); ++i) {
Generate(sig->GetParam(i), data);
}
// Emit call.
builder_->EmitWithU32V(kExprCallFunction, func_index);
// Convert the return value to the wanted type.
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.
Generate(wanted_type, data);
} else if (return_type != kWasmStmt && wanted_type == kWasmStmt) {
// 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);
}
}
template <ValueType T1, ValueType T2> template <ValueType T1, ValueType T2>
void sequence(DataRange& data) { void sequence(DataRange& data) {
Generate<T1, T2>(data); Generate<T1, T2>(data);
...@@ -239,9 +294,12 @@ class WasmGenerator { ...@@ -239,9 +294,12 @@ class WasmGenerator {
}; };
public: public:
explicit WasmGenerator(WasmFunctionBuilder* fn) : builder_(fn) { explicit WasmGenerator(WasmFunctionBuilder* fn,
DCHECK_EQ(1, fn->signature()->return_count()); const std::vector<FunctionSig*>& functions)
blocks_.push_back(fn->signature()->GetReturn(0)); : builder_(fn), functions_(functions) {
FunctionSig* sig = fn->signature();
DCHECK_GE(1, sig->return_count());
blocks_.push_back(sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(0));
} }
void Generate(ValueType type, DataRange& data); void Generate(ValueType type, DataRange& data);
...@@ -260,6 +318,7 @@ class WasmGenerator { ...@@ -260,6 +318,7 @@ class WasmGenerator {
private: private:
WasmFunctionBuilder* builder_; WasmFunctionBuilder* builder_;
std::vector<ValueType> blocks_; std::vector<ValueType> blocks_;
const std::vector<FunctionSig*>& functions_;
uint32_t recursion_depth = 0; uint32_t recursion_depth = 0;
static constexpr uint32_t kMaxRecursionDepth = 64; static constexpr uint32_t kMaxRecursionDepth = 64;
...@@ -289,7 +348,9 @@ void WasmGenerator::Generate<kWasmStmt>(DataRange& data) { ...@@ -289,7 +348,9 @@ void WasmGenerator::Generate<kWasmStmt>(DataRange& data) {
&WasmGenerator::memop<kExprF32StoreMem, kWasmF32>, &WasmGenerator::memop<kExprF32StoreMem, kWasmF32>,
&WasmGenerator::memop<kExprF64StoreMem, kWasmF64>, &WasmGenerator::memop<kExprF64StoreMem, kWasmF64>,
&WasmGenerator::drop}; &WasmGenerator::drop,
&WasmGenerator::call<kWasmStmt>};
GenerateOneOf(alternates, data); GenerateOneOf(alternates, data);
} }
...@@ -370,7 +431,9 @@ void WasmGenerator::Generate<kWasmI32>(DataRange& data) { ...@@ -370,7 +431,9 @@ void WasmGenerator::Generate<kWasmI32>(DataRange& data) {
&WasmGenerator::memop<kExprI32LoadMem16U>, &WasmGenerator::memop<kExprI32LoadMem16U>,
&WasmGenerator::current_memory, &WasmGenerator::current_memory,
&WasmGenerator::grow_memory}; &WasmGenerator::grow_memory,
&WasmGenerator::call<kWasmI32>};
GenerateOneOf(alternates, data); GenerateOneOf(alternates, data);
} }
...@@ -417,7 +480,9 @@ void WasmGenerator::Generate<kWasmI64>(DataRange& data) { ...@@ -417,7 +480,9 @@ void WasmGenerator::Generate<kWasmI64>(DataRange& data) {
&WasmGenerator::memop<kExprI64LoadMem16S>, &WasmGenerator::memop<kExprI64LoadMem16S>,
&WasmGenerator::memop<kExprI64LoadMem16U>, &WasmGenerator::memop<kExprI64LoadMem16U>,
&WasmGenerator::memop<kExprI64LoadMem32S>, &WasmGenerator::memop<kExprI64LoadMem32S>,
&WasmGenerator::memop<kExprI64LoadMem32U>}; &WasmGenerator::memop<kExprI64LoadMem32U>,
&WasmGenerator::call<kWasmI64>};
GenerateOneOf(alternates, data); GenerateOneOf(alternates, data);
} }
...@@ -440,7 +505,9 @@ void WasmGenerator::Generate<kWasmF32>(DataRange& data) { ...@@ -440,7 +505,9 @@ void WasmGenerator::Generate<kWasmF32>(DataRange& data) {
&WasmGenerator::block<kWasmF32>, &WasmGenerator::block<kWasmF32>,
&WasmGenerator::loop<kWasmF32>, &WasmGenerator::loop<kWasmF32>,
&WasmGenerator::memop<kExprF32LoadMem>}; &WasmGenerator::memop<kExprF32LoadMem>,
&WasmGenerator::call<kWasmF32>};
GenerateOneOf(alternates, data); GenerateOneOf(alternates, data);
} }
...@@ -463,7 +530,9 @@ void WasmGenerator::Generate<kWasmF64>(DataRange& data) { ...@@ -463,7 +530,9 @@ void WasmGenerator::Generate<kWasmF64>(DataRange& data) {
&WasmGenerator::block<kWasmF64>, &WasmGenerator::block<kWasmF64>,
&WasmGenerator::loop<kWasmF64>, &WasmGenerator::loop<kWasmF64>,
&WasmGenerator::memop<kExprF64LoadMem>}; &WasmGenerator::memop<kExprF64LoadMem>,
&WasmGenerator::call<kWasmF64>};
GenerateOneOf(alternates, data); GenerateOneOf(alternates, data);
} }
...@@ -489,6 +558,19 @@ void WasmGenerator::Generate(ValueType type, DataRange& data) { ...@@ -489,6 +558,19 @@ void WasmGenerator::Generate(ValueType type, DataRange& data) {
UNREACHABLE(); UNREACHABLE();
} }
} }
FunctionSig* GenerateSig(Zone* zone, DataRange& data) {
// Generate enough parameters to spill some to the stack.
constexpr int kMaxParameters = 15;
int num_params = int{data.get<uint8_t>()} % (kMaxParameters + 1);
bool has_return = data.get<bool>();
FunctionSig::Builder builder(zone, has_return ? 1 : 0, num_params);
if (has_return) builder.AddReturn(GetValueType(data));
for (int i = 0; i < num_params; ++i) builder.AddParam(GetValueType(data));
return builder.Build();
}
} // namespace } // namespace
class WasmCompileFuzzer : public WasmExecutionFuzzer { class WasmCompileFuzzer : public WasmExecutionFuzzer {
...@@ -501,14 +583,32 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer { ...@@ -501,14 +583,32 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
WasmModuleBuilder builder(zone); WasmModuleBuilder builder(zone);
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
WasmGenerator gen(f);
DataRange range(data, static_cast<uint32_t>(size)); DataRange range(data, static_cast<uint32_t>(size));
gen.Generate<kWasmI32>(range); std::vector<FunctionSig*> function_signatures;
function_signatures.push_back(sigs.i_iii());
f->Emit(kExprEnd); static_assert(kMaxFunctions >= 1, "need min. 1 function");
builder.AddExport(CStrVector("main"), f); int num_functions = 1 + (range.get<uint8_t>() % kMaxFunctions);
for (int i = 1; i < num_functions; ++i) {
function_signatures.push_back(GenerateSig(zone, range));
}
for (int i = 0; i < num_functions; ++i) {
DataRange function_range =
i == num_functions - 1 ? std::move(range) : range.split();
FunctionSig* sig = function_signatures[i];
WasmFunctionBuilder* f = builder.AddFunction(sig);
WasmGenerator gen(f, function_signatures);
ValueType return_type =
sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(0);
gen.Generate(return_type, function_range);
f->Emit(kExprEnd);
if (i == 0) builder.AddExport(CStrVector("main"), f);
}
builder.SetMaxMemorySize(32); builder.SetMaxMemorySize(32);
builder.WriteTo(buffer); builder.WriteTo(buffer);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment