Commit 52acb3d2 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[fuzzer] Add support for reference types and simd

Changes:
- Add reference types and simd to GetValueType().
- Generalize BlockScope to handle reference types. Add EmitValueType()
  to WasmFunctionBuilder.
- Constrain local_op and global_op to non-simd numeric types.
- Add GenerateOneOf() for functions that need a heap type. Add
  GenerateOptRef(). Add ref_null, get_local_opt_ref as options for
  GenerateOptRef().
- Remove the numeric conversion logic from ConsumeAndGenerate.

Bug: v8:11954
Change-Id: Idebae4a537326bdc03ac2f5e9c69a519f196938c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3009456
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarThibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75614}
parent 394c57a8
......@@ -120,6 +120,23 @@ void WasmFunctionBuilder::EmitWithU32V(WasmOpcode opcode, uint32_t immediate) {
body_.write_u32v(immediate);
}
namespace {
void WriteValueType(ZoneBuffer* buffer, const ValueType& type) {
buffer->write_u8(type.value_type_code());
if (type.encoding_needs_heap_type()) {
buffer->write_i32v(type.heap_type().code());
}
if (type.is_rtt()) {
if (type.has_depth()) buffer->write_u32v(type.depth());
buffer->write_u32v(type.ref_index());
}
}
} // namespace
void WasmFunctionBuilder::EmitValueType(ValueType type) {
WriteValueType(&body_, type);
}
void WasmFunctionBuilder::EmitI32Const(int32_t value) {
EmitWithI32V(kExprI32Const, value);
}
......@@ -437,17 +454,6 @@ void WasmModuleBuilder::SetMaxMemorySize(uint32_t value) {
void WasmModuleBuilder::SetHasSharedMemory() { has_shared_memory_ = true; }
namespace {
void WriteValueType(ZoneBuffer* buffer, const ValueType& type) {
buffer->write_u8(type.value_type_code());
if (type.encoding_needs_heap_type()) {
buffer->write_i32v(type.heap_type().code());
}
if (type.is_rtt()) {
if (type.has_depth()) buffer->write_u32v(type.depth());
buffer->write_u32v(type.ref_index());
}
}
void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init,
ValueType type) {
switch (init.kind()) {
......@@ -507,11 +513,16 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init,
break;
case kOptRef:
buffer->write_u8(kExprRefNull);
buffer->write_i32v(type.heap_type().code());
break;
case kS128:
buffer->write_u8(static_cast<byte>(kSimdPrefix));
buffer->write_u8(static_cast<byte>(kExprS128Const & 0xff));
for (int i = 0; i < kSimd128Size; i++) buffer->write_u8(0);
break;
case kI8:
case kI16:
case kVoid:
case kS128:
case kBottom:
case kRef:
case kRtt:
......
......@@ -186,6 +186,7 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
void EmitWithI32V(WasmOpcode opcode, int32_t immediate);
void EmitWithU32V(WasmOpcode opcode, uint32_t immediate);
void EmitValueType(ValueType type);
void EmitDirectCallIndex(uint32_t index);
void SetName(base::Vector<const char> name);
void AddAsmWasmOffset(size_t call_position, size_t to_number_position);
......
......@@ -3153,6 +3153,9 @@ class WasmInterpreterInternals {
switch (sig->GetParam(i).heap_representation()) {
case HeapType::kExtern:
case HeapType::kFunc:
case HeapType::kEq:
case HeapType::kData:
case HeapType::kI31:
case HeapType::kAny: {
Handle<Object> ref = value.to_ref();
encoded_values->set(encoded_index++, *ref);
......@@ -3160,9 +3163,6 @@ class WasmInterpreterInternals {
}
case HeapType::kBottom:
UNREACHABLE();
case HeapType::kEq:
case HeapType::kData:
case HeapType::kI31:
default:
// TODO(7748): Implement these.
UNIMPLEMENTED();
......@@ -3274,6 +3274,9 @@ class WasmInterpreterInternals {
switch (sig->GetParam(i).heap_representation()) {
case HeapType::kExtern:
case HeapType::kFunc:
case HeapType::kEq:
case HeapType::kData:
case HeapType::kI31:
case HeapType::kAny: {
Handle<Object> ref(encoded_values->get(encoded_index++),
isolate_);
......
......@@ -90,20 +90,14 @@ class DataRange {
};
ValueType GetValueType(DataRange* data) {
// TODO(v8:8460): We do not add kWasmS128 here yet because this method is used
// to generate globals, and since we do not have v128.const yet, there is no
// way to specify an initial value a global of this type.
switch (data->get<uint8_t>() % 4) {
case 0:
return kWasmI32;
case 1:
return kWasmI64;
case 2:
return kWasmF32;
case 3:
return kWasmF64;
}
UNREACHABLE();
constexpr ValueType types[] = {
kWasmI32, kWasmI64,
kWasmF32, kWasmF64,
kWasmS128, kWasmExternRef,
kWasmFuncRef, kWasmEqRef,
kWasmAnyRef, ValueType::Ref(HeapType(HeapType::kData), kNullable)};
return types[data->get<uint8_t>() % arraysize(types)];
}
class WasmGenerator {
......@@ -121,13 +115,14 @@ class WasmGenerator {
base::Vector<const ValueType> br_types, bool emit_end = true)
: gen_(gen), emit_end_(emit_end) {
gen->blocks_.emplace_back(br_types.begin(), br_types.end());
gen->builder_->EmitByte(block_type);
if (param_types.size() == 0 && result_types.size() == 0) {
gen->builder_->EmitWithU8(block_type, kWasmVoid.value_type_code());
gen->builder_->EmitValueType(kWasmVoid);
return;
}
if (param_types.size() == 0 && result_types.size() == 1) {
gen->builder_->EmitWithU8(block_type,
result_types[0].value_type_code());
gen->builder_->EmitValueType(result_types[0]);
return;
}
// Multi-value block.
......@@ -144,7 +139,7 @@ class WasmGenerator {
}
FunctionSig* sig = builder.Build();
int sig_id = gen->builder_->builder()->AddSignature(sig);
gen->builder_->EmitWithI32V(block_type, sig_id);
gen->builder_->EmitI32V(sig_id);
}
~BlockScope() {
......@@ -533,17 +528,6 @@ class WasmGenerator {
builder_->Emit(kConvertOpcodes[arr_idx]);
}
void ConvertOrGenerate(ValueType src, ValueType dst, DataRange* data) {
if (src == dst) return;
if (src == kWasmVoid && dst != kWasmVoid) {
Generate(dst, data);
} else if (dst == kWasmVoid && src != kWasmVoid) {
builder_->Emit(kExprDrop);
} else {
Convert(src, dst);
}
}
void call(DataRange* data, ValueType wanted_kind, CallDirect call_direct) {
uint8_t random_byte = data->get<uint8_t>();
int func_index = random_byte % functions_.size();
......@@ -616,12 +600,17 @@ class WasmGenerator {
return {index, type};
}
constexpr static bool is_convertible_kind(ValueKind kind) {
return kind == kI32 || kind == kI64 || kind == kF32 || kind == kF64;
}
template <ValueKind wanted_kind>
void local_op(DataRange* data, WasmOpcode opcode) {
STATIC_ASSERT(wanted_kind == kVoid || is_convertible_kind(wanted_kind));
Var local = GetRandomLocal(data);
// If there are no locals and no parameters, just generate any value (if a
// value is needed), or do nothing.
if (!local.is_valid()) {
if (!local.is_valid() || !is_convertible_kind(local.type.kind())) {
if (wanted_kind == kVoid) return;
return Generate<wanted_kind>(data);
}
......@@ -671,11 +660,12 @@ class WasmGenerator {
template <ValueKind wanted_kind>
void global_op(DataRange* data) {
STATIC_ASSERT(wanted_kind == kVoid || is_convertible_kind(wanted_kind));
constexpr bool is_set = wanted_kind == kVoid;
Var global = GetRandomGlobal(data, is_set);
// If there are no globals, just generate any value (if a value is needed),
// or do nothing.
if (!global.is_valid()) {
if (!global.is_valid() || !is_convertible_kind(global.type.kind())) {
if (wanted_kind == kVoid) return;
return Generate<wanted_kind>(data);
}
......@@ -736,7 +726,23 @@ class WasmGenerator {
void grow_memory(DataRange* data);
void ref_null(HeapType type, DataRange* data) {
builder_->EmitWithI32V(kExprRefNull, type.code());
}
void get_local_opt_ref(HeapType type, DataRange* data) {
Var local = GetRandomLocal(data);
// TODO(manoskouk): Ideally we would check for subtyping here over type
// equality, but we don't have a module.
if (local.is_valid() && local.type.is_object_reference() &&
type == local.type.heap_type()) {
builder_->EmitWithU32V(kExprLocalGet, local.index);
} else {
ref_null(type, data);
}
}
using GenerateFn = void (WasmGenerator::*const)(DataRange*);
using GenerateFnWithHeap = void (WasmGenerator::*const)(HeapType, DataRange*);
template <size_t N>
void GenerateOneOf(GenerateFn (&alternatives)[N], DataRange* data) {
......@@ -748,6 +754,17 @@ class WasmGenerator {
(this->*alternate)(data);
}
template <size_t N>
void GenerateOneOf(GenerateFnWithHeap (&alternatives)[N], HeapType type,
DataRange* data) {
static_assert(N < std::numeric_limits<uint8_t>::max(),
"Too many alternatives. Use a bigger type if needed.");
const auto which = data->get<uint8_t>();
GenerateFnWithHeap alternate = alternatives[which % N];
(this->*alternate)(type, data);
}
struct GeneratorRecursionScope {
explicit GeneratorRecursionScope(WasmGenerator* gen) : gen(gen) {
++gen->recursion_depth;
......@@ -795,6 +812,8 @@ class WasmGenerator {
Generate<T2, Ts...>(data);
}
void GenerateOptRef(HeapType type, DataRange* data);
std::vector<ValueType> GenerateTypes(DataRange* data);
void Generate(base::Vector<const ValueType> types, DataRange* data);
void ConsumeAndGenerate(base::Vector<const ValueType> parameter_types,
......@@ -1524,11 +1543,20 @@ void WasmGenerator::Generate(ValueType type, DataRange* data) {
return Generate<kF64>(data);
case kS128:
return Generate<kS128>(data);
case kOptRef:
return GenerateOptRef(type.heap_type(), data);
default:
UNREACHABLE();
}
}
void WasmGenerator::GenerateOptRef(HeapType type, DataRange* data) {
constexpr GenerateFnWithHeap alternatives[] = {
&WasmGenerator::ref_null, &WasmGenerator::get_local_opt_ref};
GenerateOneOf(alternatives, type, data);
}
std::vector<ValueType> WasmGenerator::GenerateTypes(DataRange* data) {
std::vector<ValueType> types;
int num_params = int{data->get<uint8_t>()} % (kMaxParameters + 1);
......@@ -1575,33 +1603,14 @@ void WasmGenerator::Generate(base::Vector<const ValueType> types,
}
// Emit code to match an arbitrary signature.
// TODO(manoskouk): Do something which uses inputs instead of dropping them.
// Possibly generate function a function with the correct sig on the fly? Or
// generalize the {Convert} function.
void WasmGenerator::ConsumeAndGenerate(
base::Vector<const ValueType> param_types,
base::Vector<const ValueType> return_types, DataRange* data) {
if (param_types.size() == 0) {
for (unsigned i = 0; i < param_types.size(); i++) builder_->Emit(kExprDrop);
Generate(return_types, data);
return;
}
// Keep exactly one of the parameters on the stack with a combination of drops
// and selects, convert this value to the first return type, and generate the
// remaining types.
// TODO(thibaudm): Improve this strategy to potentially generate any sequence
// of instructions matching the given signature.
size_t return_index = data->get<uint8_t>() % param_types.size();
for (size_t i = param_types.size() - 1; i > return_index; --i) {
builder_->Emit(kExprDrop);
}
for (size_t i = return_index; i > 0; --i) {
Convert(param_types[i], param_types[i - 1]);
builder_->EmitI32Const(0);
builder_->Emit(kExprSelect);
}
if (return_types.empty()) {
builder_->Emit(kExprDrop);
} else {
Convert(param_types[0], return_types[0]);
Generate(return_types + 1, data);
}
}
enum SigKind { kFunctionSig, kExceptionSig };
......@@ -1697,6 +1706,9 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
constexpr bool require_valid = true;
EXPERIMENTAL_FLAG_SCOPE(reftypes);
EXPERIMENTAL_FLAG_SCOPE(typed_funcref);
EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(simd);
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmCompileFuzzer().FuzzWasmModule({data, size}, require_valid);
return 0;
......
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