diff --git a/BUILD.gn b/BUILD.gn index 4f626ae97d..98e035fc6a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -2838,6 +2838,7 @@ group("v8_fuzzers") { testonly = true deps = [ ":v8_simple_json_fuzzer", + ":v8_simple_multi_return_fuzzer", ":v8_simple_parser_fuzzer", ":v8_simple_regexp_fuzzer", ":v8_simple_wasm_async_fuzzer", @@ -3088,6 +3089,24 @@ v8_source_set("json_fuzzer") { v8_fuzzer("json_fuzzer") { } +v8_source_set("multi_return_fuzzer") { + sources = [ + "test/fuzzer/multi-return.cc", + ] + + deps = [ + ":fuzzer_support", + ] + + configs = [ + ":external_config", + ":internal_config_base", + ] +} + +v8_fuzzer("multi_return_fuzzer") { +} + v8_source_set("parser_fuzzer") { sources = [ "test/fuzzer/parser.cc", diff --git a/src/arm/simulator-arm.h b/src/arm/simulator-arm.h index f7b25fc2e8..1cb11ffd96 100644 --- a/src/arm/simulator-arm.h +++ b/src/arm/simulator-arm.h @@ -89,7 +89,7 @@ class Simulator : public SimulatorBase { // The currently executing Simulator instance. Potentially there can be one // for each native thread. - static Simulator* current(v8::internal::Isolate* isolate); + V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate); // Accessors for register state. Reading the pc value adheres to the ARM // architecture specification and is off by a 8 from the currently executing @@ -211,7 +211,8 @@ class Simulator : public SimulatorBase { end_sim_pc = -2 }; - intptr_t CallImpl(byte* entry, int argument_count, const intptr_t* arguments); + V8_EXPORT_PRIVATE intptr_t CallImpl(byte* entry, int argument_count, + const intptr_t* arguments); intptr_t CallFPImpl(byte* entry, double d0, double d1); // Unsupported instructions use Format to print an error and stop execution. diff --git a/src/arm64/simulator-arm64.h b/src/arm64/simulator-arm64.h index b210f2a0e6..68f93c11e9 100644 --- a/src/arm64/simulator-arm64.h +++ b/src/arm64/simulator-arm64.h @@ -660,7 +660,7 @@ class Simulator : public DecoderVisitor, public SimulatorBase { // System functions. - static Simulator* current(v8::internal::Isolate* isolate); + V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate); // A wrapper class that stores an argument for one of the above Call // functions. @@ -2278,7 +2278,7 @@ class Simulator : public DecoderVisitor, public SimulatorBase { private: void Init(FILE* stream); - void CallImpl(byte* entry, CallArgument* args); + V8_EXPORT_PRIVATE void CallImpl(byte* entry, CallArgument* args); // Read floating point return values. template diff --git a/src/compiler/pipeline.h b/src/compiler/pipeline.h index 2dca7794eb..b5b6b5f142 100644 --- a/src/compiler/pipeline.h +++ b/src/compiler/pipeline.h @@ -76,7 +76,7 @@ class Pipeline : public AllStatic { // Run the pipeline on a machine graph and generate code. If {schedule} is // {nullptr}, then compute a new schedule for code generation. - static Handle GenerateCodeForTesting( + V8_EXPORT_PRIVATE static Handle GenerateCodeForTesting( CompilationInfo* info, Isolate* isolate, CallDescriptor* call_descriptor, Graph* graph, Schedule* schedule = nullptr, SourcePositionTable* source_positions = nullptr); diff --git a/src/machine-type.h b/src/machine-type.h index 4502b2fdc2..63e3c7a462 100644 --- a/src/machine-type.h +++ b/src/machine-type.h @@ -39,7 +39,7 @@ static_assert(static_cast(MachineRepresentation::kLastRepresentation) < kIntSize * kBitsPerByte, "Bit masks of MachineRepresentation should fit in an int"); -const char* MachineReprToString(MachineRepresentation); +V8_EXPORT_PRIVATE const char* MachineReprToString(MachineRepresentation); enum class MachineSemantic : uint8_t { kNone, diff --git a/src/mips/simulator-mips.h b/src/mips/simulator-mips.h index 1e7fa7c1dd..0c417becd5 100644 --- a/src/mips/simulator-mips.h +++ b/src/mips/simulator-mips.h @@ -171,7 +171,7 @@ class Simulator : public SimulatorBase { // The currently executing Simulator instance. Potentially there can be one // for each native thread. - static Simulator* current(v8::internal::Isolate* isolate); + V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate); // Accessors for register state. Reading the pc value adheres to the MIPS // architecture specification and is off by a 8 from the currently executing @@ -279,7 +279,8 @@ class Simulator : public SimulatorBase { Unpredictable = 0xbadbeaf }; - intptr_t CallImpl(byte* entry, int argument_count, const intptr_t* arguments); + V8_EXPORT_PRIVATE intptr_t CallImpl(byte* entry, int argument_count, + const intptr_t* arguments); // Unsupported instructions use Format to print an error and stop execution. void Format(Instruction* instr, const char* format); diff --git a/src/mips64/simulator-mips64.h b/src/mips64/simulator-mips64.h index 9850660e4c..c4292236b0 100644 --- a/src/mips64/simulator-mips64.h +++ b/src/mips64/simulator-mips64.h @@ -171,7 +171,7 @@ class Simulator : public SimulatorBase { // The currently executing Simulator instance. Potentially there can be one // for each native thread. - static Simulator* current(v8::internal::Isolate* isolate); + V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate); // Accessors for register state. Reading the pc value adheres to the MIPS // architecture specification and is off by a 8 from the currently executing @@ -281,7 +281,8 @@ class Simulator : public SimulatorBase { Unpredictable = 0xbadbeaf }; - intptr_t CallImpl(byte* entry, int argument_count, const intptr_t* arguments); + V8_EXPORT_PRIVATE intptr_t CallImpl(byte* entry, int argument_count, + const intptr_t* arguments); // Unsupported instructions use Format to print an error and stop execution. void Format(Instruction* instr, const char* format); diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status index 9ba10fb12c..8941c4fea3 100644 --- a/test/cctest/cctest.status +++ b/test/cctest/cctest.status @@ -123,9 +123,6 @@ 'test-serialize/CustomSnapshotDataBlobImmortalImmovableRoots': [PASS, ['mode == debug', SKIP]], 'test-parsing/ObjectRestNegativeTestSlow': [PASS, ['mode == debug', SKIP]], - # todo(ahaas): Flaky test. I want to remove this test eventually but keep it - # for now for debugging. - 'test-multiple-return/ReturnMultipleRandom': [SKIP], }], # ALWAYS ############################################################################## diff --git a/test/cctest/compiler/test-multiple-return.cc b/test/cctest/compiler/test-multiple-return.cc index 37af1cc0c5..b5b7e5b650 100644 --- a/test/cctest/compiler/test-multiple-return.cc +++ b/test/cctest/compiler/test-multiple-return.cc @@ -9,7 +9,6 @@ #include "src/assembler.h" #include "src/base/bits.h" -#include "src/base/utils/random-number-generator.h" #include "src/codegen.h" #include "src/compiler.h" #include "src/compiler/linkage.h" @@ -26,18 +25,10 @@ namespace compiler { namespace { -int index(MachineType type) { return static_cast(type.representation()); } - int size(MachineType type) { return 1 << ElementSizeLog2Of(type.representation()); } -bool is_float(MachineType type) { - MachineRepresentation rep = type.representation(); - return rep == MachineRepresentation::kFloat32 || - rep == MachineRepresentation::kFloat64; -} - int num_registers(MachineType type) { const RegisterConfiguration* config = RegisterConfiguration::Default(); switch (type.representation()) { @@ -119,81 +110,6 @@ CallDescriptor* CreateMonoCallDescriptor(Zone* zone, int return_count, stack_returns); // on-stack return count } -MachineType RandomType(v8::base::RandomNumberGenerator* rng) { - switch (rng->NextInt(4)) { - case 0: -#if (!V8_TARGET_ARCH_32_BIT) - return MachineType::Int64(); -// Else fall through. -#endif - case 1: - return MachineType::Int32(); - case 2: - return MachineType::Float32(); - case 3: - return MachineType::Float64(); - default: - UNREACHABLE(); - } -} - -LinkageLocation alloc(MachineType type, int* int_count, int* float_count, - int* stack_slots) { - int* count = is_float(type) ? float_count : int_count; - LinkageLocation location = LinkageLocation::ForAnyRegister(); // Dummy. - if (*count < num_registers(type)) { - location = LinkageLocation::ForRegister(codes(type)[*count], type); - } else { - location = LinkageLocation::ForCallerFrameSlot(-*stack_slots - 1, type); - *stack_slots += std::max(1, size(type) / kPointerSize); - } - ++*count; - return location; -} - -CallDescriptor* CreateRandomCallDescriptor( - Zone* zone, int return_count, int param_count, - v8::base::RandomNumberGenerator* rng) { - LocationSignature::Builder locations(zone, return_count, param_count); - - int stack_slots = 0; - int int_params = 0; - int float_params = 0; - for (int i = 0; i < param_count; i++) { - MachineType type = RandomType(rng); - LinkageLocation location = - alloc(type, &int_params, &float_params, &stack_slots); - locations.AddParam(location); - } - int stack_params = stack_slots; - - int int_returns = 0; - int float_returns = 0; - for (int i = 0; i < return_count; i++) { - MachineType type = RandomType(rng); - LinkageLocation location = - alloc(type, &int_returns, &float_returns, &stack_slots); - locations.AddReturn(location); - } - int stack_returns = stack_slots - stack_params; - - MachineType target_type = MachineType::AnyTagged(); - LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type); - return new (zone) CallDescriptor( // -- - CallDescriptor::kCallCodeObject, // kind - target_type, // target MachineType - target_loc, // target location - locations.Build(), // location_sig - stack_params, // on-stack parameter count - compiler::Operator::kNoProperties, // properties - 0, // callee-saved registers - 0, // callee-saved fp regs - CallDescriptor::kNoFlags, // flags - "c-call", // debug name - 0, // allocatable registers - stack_returns); // on-stack return count -} - } // namespace Node* Constant(RawMachineAssembler& m, MachineType type, int value) { @@ -354,129 +270,6 @@ TEST_MULTI(Float64, MachineType::Float64()) #undef TEST_MULTI -TEST(ReturnMultipleRandom) { - // TODO(titzer): Test without RNG? - v8::base::RandomNumberGenerator* rng(CcTest::random_number_generator()); - - const int kNumberOfRuns = 10; - for (int run = 0; run < kNumberOfRuns; ++run) { - printf("\n==== Run %d ====\n\n", run); - - v8::internal::AccountingAllocator allocator; - Zone zone(&allocator, ZONE_NAME); - - // Create randomized descriptor. - int param_count = rng->NextInt(20); - int return_count = rng->NextInt(10); - CallDescriptor* desc = - CreateRandomCallDescriptor(&zone, return_count, param_count, rng); - - printf("["); - for (size_t j = 0; j < desc->ParameterCount(); ++j) { - printf(" %s", - MachineReprToString(desc->GetParameterType(j).representation())); - } - printf(" ] -> ["); - for (size_t j = 0; j < desc->ReturnCount(); ++j) { - printf(" %s", - MachineReprToString(desc->GetReturnType(j).representation())); - } - printf(" ]\n\n"); - - // Count parameters of each type. - const size_t num_types = - static_cast(MachineRepresentation::kLastRepresentation) + 1; - std::unique_ptr counts(new int[num_types]); - for (size_t i = 0; i < num_types; ++i) { - counts[i] = 0; - } - for (size_t i = 0; i < desc->ParameterCount(); ++i) { - ++counts[index(desc->GetParameterType(i))]; - } - - // Generate random inputs. - std::unique_ptr inputs(new int[desc->ParameterCount()]); - std::unique_ptr outputs(new int[desc->ReturnCount()]); - for (size_t i = 0; i < desc->ParameterCount(); ++i) { - inputs[i] = rng->NextInt(10000); - } - - HandleAndZoneScope handles; - RawMachineAssembler m(handles.main_isolate(), - new (handles.main_zone()) Graph(handles.main_zone()), - desc, MachineType::PointerRepresentation(), - InstructionSelector::SupportedMachineOperatorFlags()); - - // Generate Callee, returning random picks of its parameters. - typedef Node* Node_ptr; - std::unique_ptr params( - new Node_ptr[desc->ParameterCount() + 1]); - std::unique_ptr returns(new Node_ptr[desc->ReturnCount()]); - for (size_t i = 0; i < desc->ParameterCount(); ++i) { - params[i] = m.Parameter(i); - } - for (size_t i = 0; i < desc->ReturnCount(); ++i) { - MachineType type = desc->GetReturnType(i); - // Find a random same-type parameter to return. Use a constant if none. - if (counts[index(type)] == 0) { - returns[i] = Constant(m, type, 42); - outputs[i] = 42; - } else { - int n = rng->NextInt(counts[index(type)]); - int k; - for (k = 0;; ++k) { - if (desc->GetParameterType(k) == desc->GetReturnType(i) && --n < 0) { - break; - } - } - returns[i] = params[k]; - outputs[i] = inputs[k]; - } - } - m.Return(static_cast(desc->ReturnCount()), returns.get()); - - CompilationInfo info(ArrayVector("testing"), handles.main_zone(), - Code::STUB); - Handle code = Pipeline::GenerateCodeForTesting( - &info, handles.main_isolate(), desc, m.graph(), m.Export()); -#ifdef ENABLE_DISASSEMBLER - if (FLAG_print_code) { - OFStream os(stdout); - code->Disassemble("multi_value", os); - } -#endif - - // Generate caller. - int expect = 0; - RawMachineAssemblerTester mt; - params[0] = mt.HeapConstant(code); - for (size_t i = 0; i < desc->ParameterCount(); ++i) { - params[i + 1] = Constant(mt, desc->GetParameterType(i), inputs[i]); - } - Node* ret_multi = - mt.AddNode(mt.common()->Call(desc), - static_cast(desc->ParameterCount() + 1), params.get()); - Node* ret = Constant(mt, MachineType::Int32(), 0); - for (size_t i = 0; i < desc->ReturnCount(); ++i) { - if (rng->NextInt(3) == 0) continue; // Skip random outputs. - Node* x = (desc->ReturnCount() == 1) - ? ret_multi - : mt.AddNode(mt.common()->Projection(i), ret_multi); - ret = mt.Int32Add(ret, ToInt32(mt, desc->GetReturnType(i), x)); - expect += outputs[i]; - } - mt.Return(ret); -#ifdef ENABLE_DISASSEMBLER - Handle code2 = mt.GetCode(); - if (FLAG_print_code) { - OFStream os(stdout); - code2->Disassemble("multi_value_call", os); - } -#endif - CHECK_EQ(expect, mt.Call()); - } -} - void ReturnLastValue(MachineType type) { v8::internal::AccountingAllocator allocator; Zone zone(&allocator, ZONE_NAME); diff --git a/test/fuzzer/fuzzer.gyp b/test/fuzzer/fuzzer.gyp index 3d76018d55..0c54211290 100644 --- a/test/fuzzer/fuzzer.gyp +++ b/test/fuzzer/fuzzer.gyp @@ -89,6 +89,36 @@ 'regexp.cc', ], }, + { + 'target_name': 'v8_simple_multi_return_fuzzer', + 'type': 'executable', + 'dependencies': [ + 'multi_return_fuzzer_lib', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'fuzzer.cc', + ], + }, + { + 'target_name': 'multi_return_fuzzer_lib', + 'type': 'static_library', + 'dependencies': [ + '../../src/v8.gyp:v8_libplatform', + 'fuzzer_support', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ ### gcmole(all) ### + '../compiler/c-signature.h', + '../compiler/call-helper.h', + '../compiler/raw-machine-assembler-tester.h', + 'multi-return.cc', + ], + }, { 'target_name': 'v8_simple_wasm_fuzzer', 'type': 'executable', diff --git a/test/fuzzer/fuzzer.isolate b/test/fuzzer/fuzzer.isolate index 097d55885d..9391dcc7c0 100644 --- a/test/fuzzer/fuzzer.isolate +++ b/test/fuzzer/fuzzer.isolate @@ -8,6 +8,7 @@ '<(PRODUCT_DIR)/v8_simple_json_fuzzer<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/v8_simple_parser_fuzzer<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/v8_simple_regexp_fuzzer<(EXECUTABLE_SUFFIX)', + '<(PRODUCT_DIR)/v8_simple_multi_return_fuzzer<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/v8_simple_wasm_fuzzer<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/v8_simple_wasm_async_fuzzer<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/v8_simple_wasm_call_fuzzer<(EXECUTABLE_SUFFIX)', @@ -25,6 +26,7 @@ './json/', './parser/', './regexp/', + './multi_return/', './wasm/', './wasm_async/', './wasm_call/', diff --git a/test/fuzzer/multi-return.cc b/test/fuzzer/multi-return.cc new file mode 100644 index 0000000000..1a39792cec --- /dev/null +++ b/test/fuzzer/multi-return.cc @@ -0,0 +1,341 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "src/compilation-info.h" +#include "src/compiler/graph.h" +#include "src/compiler/instruction-selector.h" +#include "src/compiler/linkage.h" +#include "src/compiler/node.h" +#include "src/compiler/operator.h" +#include "src/compiler/pipeline.h" +#include "src/compiler/raw-machine-assembler.h" +#include "src/machine-type.h" +#include "src/objects-inl.h" +#include "src/objects.h" +#include "src/simulator.h" +#include "src/zone/accounting-allocator.h" +#include "src/zone/zone.h" +#include "test/fuzzer/fuzzer-support.h" + +namespace v8 { +namespace internal { +namespace compiler { +namespace fuzzer { + +constexpr MachineType kTypes[] = { + // The first entry is just a placeholder, because '0' is a separator. + MachineType(), +#if !V8_TARGET_ARCH_32_BIT + MachineType::Int64(), +#endif + MachineType::Int32(), MachineType::Float32(), MachineType::Float64()}; + +static constexpr int kNumTypes = arraysize(kTypes); + +class InputProvider { + public: + InputProvider(const uint8_t* data, size_t size) + : current_(data), end_(data + size) {} + + size_t NumNonZeroBytes(size_t offset, int limit) { + DCHECK_LE(limit, std::numeric_limits::max()); + DCHECK_GE(current_ + offset, current_); + const uint8_t* p; + for (p = current_ + offset; p < end_; ++p) { + if (*p % limit == 0) break; + } + return p - current_ - offset; + } + + int NextInt8(int limit) { + DCHECK_LE(limit, std::numeric_limits::max()); + if (current_ == end_) return 0; + uint8_t result = *current_; + current_++; + return static_cast(result) % limit; + } + + int NextInt32(int limit) { + if (current_ + sizeof(uint32_t) > end_) return 0; + int result = ReadLittleEndianValue(current_); + current_ += sizeof(uint32_t); + return result % limit; + } + + private: + const uint8_t* current_; + const uint8_t* end_; +}; + +MachineType RandomType(InputProvider* input) { + return kTypes[input->NextInt8(kNumTypes)]; +} + +int num_registers(MachineType type) { + const RegisterConfiguration* config = RegisterConfiguration::Default(); + switch (type.representation()) { + case MachineRepresentation::kWord32: + case MachineRepresentation::kWord64: + return config->num_allocatable_general_registers(); + case MachineRepresentation::kFloat32: + return config->num_allocatable_float_registers(); + case MachineRepresentation::kFloat64: + return config->num_allocatable_double_registers(); + default: + UNREACHABLE(); + } +} + +int size(MachineType type) { + return 1 << ElementSizeLog2Of(type.representation()); +} + +int index(MachineType type) { return static_cast(type.representation()); } + +const int* codes(MachineType type) { + const RegisterConfiguration* config = RegisterConfiguration::Default(); + switch (type.representation()) { + case MachineRepresentation::kWord32: + case MachineRepresentation::kWord64: + return config->allocatable_general_codes(); + case MachineRepresentation::kFloat32: + return config->allocatable_float_codes(); + case MachineRepresentation::kFloat64: + return config->allocatable_double_codes(); + default: + UNREACHABLE(); + } +} + +LinkageLocation AllocateLocation(MachineType type, int* int_count, + int* float_count, int* stack_slots) { + int* count = IsFloatingPoint(type.representation()) ? float_count : int_count; + int reg_code = *count; +#if V8_TARGET_ARCH_ARM + // Allocate floats using a double register, but modify the code to + // reflect how ARM FP registers alias. + if (type == MachineType::Float32()) { + reg_code *= 2; + } +#endif + LinkageLocation location = LinkageLocation::ForAnyRegister(); // Dummy. + if (reg_code < num_registers(type)) { + location = LinkageLocation::ForRegister(codes(type)[reg_code], type); + } else { + location = LinkageLocation::ForCallerFrameSlot(-*stack_slots - 1, type); + *stack_slots += std::max(1, size(type) / kPointerSize); + } + ++*count; + return location; +} + +Node* Constant(RawMachineAssembler& m, MachineType type, int value) { + switch (type.representation()) { + case MachineRepresentation::kWord32: + return m.Int32Constant(static_cast(value)); + case MachineRepresentation::kWord64: + return m.Int64Constant(static_cast(value)); + case MachineRepresentation::kFloat32: + return m.Float32Constant(static_cast(value)); + case MachineRepresentation::kFloat64: + return m.Float64Constant(static_cast(value)); + default: + UNREACHABLE(); + } +} + +Node* ToInt32(RawMachineAssembler& m, MachineType type, Node* a) { + switch (type.representation()) { + case MachineRepresentation::kWord32: + return a; + case MachineRepresentation::kWord64: + return m.TruncateInt64ToInt32(a); + case MachineRepresentation::kFloat32: + return m.TruncateFloat32ToInt32(a); + case MachineRepresentation::kFloat64: + return m.RoundFloat64ToInt32(a); + default: + UNREACHABLE(); + } +} + +CallDescriptor* CreateRandomCallDescriptor(Zone* zone, size_t return_count, + size_t param_count, + InputProvider* input) { + LocationSignature::Builder locations(zone, return_count, param_count); + + int stack_slots = 0; + int int_params = 0; + int float_params = 0; + for (size_t i = 0; i < param_count; i++) { + MachineType type = RandomType(input); + LinkageLocation location = + AllocateLocation(type, &int_params, &float_params, &stack_slots); + locations.AddParam(location); + } + // Read the end byte of the parameters. + input->NextInt8(1); + + int stack_params = stack_slots; + int int_returns = 0; + int float_returns = 0; + for (size_t i = 0; i < return_count; i++) { + MachineType type = RandomType(input); + LinkageLocation location = + AllocateLocation(type, &int_returns, &float_returns, &stack_slots); + locations.AddReturn(location); + } + int stack_returns = stack_slots - stack_params; + + MachineType target_type = MachineType::AnyTagged(); + LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type); + return new (zone) CallDescriptor( // -- + CallDescriptor::kCallCodeObject, // kind + target_type, // target MachineType + target_loc, // target location + locations.Build(), // location_sig + stack_params, // on-stack parameter count + compiler::Operator::kNoProperties, // properties + 0, // callee-saved registers + 0, // callee-saved fp regs + CallDescriptor::kNoFlags, // flags + "c-call", // debug name + 0, // allocatable registers + stack_returns); // on-stack return count +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get(); + v8::Isolate* isolate = support->GetIsolate(); + i::Isolate* i_isolate = reinterpret_cast(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + v8::Context::Scope context_scope(support->GetContext()); + v8::TryCatch try_catch(isolate); + v8::internal::AccountingAllocator allocator; + Zone zone(&allocator, ZONE_NAME); + + InputProvider input(data, size); + // Create randomized descriptor. + size_t param_count = input.NumNonZeroBytes(0, kNumTypes); + size_t return_count = input.NumNonZeroBytes(param_count + 1, kNumTypes); + CallDescriptor* desc = + CreateRandomCallDescriptor(&zone, return_count, param_count, &input); + + if (FLAG_wasm_fuzzer_gen_test) { + // Print some debugging output which describes the produced signature. + printf("["); + for (size_t j = 0; j < desc->ParameterCount(); ++j) { + printf(" %s", + MachineReprToString(desc->GetParameterType(j).representation())); + } + printf(" ] -> ["); + for (size_t j = 0; j < desc->ReturnCount(); ++j) { + printf(" %s", + MachineReprToString(desc->GetReturnType(j).representation())); + } + printf(" ]\n\n"); + } + + // Count parameters of each type. + constexpr size_t kNumMachineRepresentations = + static_cast(MachineRepresentation::kLastRepresentation) + 1; + + // Trivial hash table for the number of occurrences of parameter types. The + // MachineRepresentation of the parameter types is used as hash code. + int counts[kNumMachineRepresentations] = {0}; + for (size_t i = 0; i < desc->ParameterCount(); ++i) { + ++counts[index(desc->GetParameterType(i))]; + } + + // Generate random inputs. + std::unique_ptr inputs(new int[desc->ParameterCount()]); + std::unique_ptr outputs(new int[desc->ReturnCount()]); + for (size_t i = 0; i < desc->ParameterCount(); ++i) { + inputs[i] = input.NextInt32(10000); + } + + RawMachineAssembler callee( + i_isolate, new (&zone) Graph(&zone), desc, + MachineType::PointerRepresentation(), + InstructionSelector::SupportedMachineOperatorFlags()); + + // Generate callee, returning random picks of its parameters. + std::unique_ptr params(new Node*[desc->ParameterCount() + 1]); + std::unique_ptr returns(new Node*[desc->ReturnCount()]); + for (size_t i = 0; i < desc->ParameterCount(); ++i) { + params[i] = callee.Parameter(i); + } + for (size_t i = 0; i < desc->ReturnCount(); ++i) { + MachineType type = desc->GetReturnType(i); + // Find a random same-type parameter to return. Use a constant if none. + if (counts[index(type)] == 0) { + returns[i] = Constant(callee, type, 42); + outputs[i] = 42; + } else { + int n = input.NextInt8(counts[index(type)]); + int k = 0; + while (desc->GetParameterType(k) != desc->GetReturnType(i) || --n > 0) { + ++k; + } + returns[i] = params[k]; + outputs[i] = inputs[k]; + } + } + callee.Return(static_cast(desc->ReturnCount()), returns.get()); + + CompilationInfo info(ArrayVector("testing"), &zone, Code::STUB); + Handle code = Pipeline::GenerateCodeForTesting( + &info, i_isolate, desc, callee.graph(), callee.Export()); + + // Generate wrapper. + int expect = 0; + + MachineSignature::Builder sig_builder(&zone, 1, 0); + sig_builder.AddReturn(MachineType::Int32()); + + CallDescriptor* wrapper_desc = + Linkage::GetSimplifiedCDescriptor(&zone, sig_builder.Build()); + RawMachineAssembler caller( + i_isolate, new (&zone) Graph(&zone), wrapper_desc, + MachineType::PointerRepresentation(), + InstructionSelector::SupportedMachineOperatorFlags()); + + params[0] = caller.HeapConstant(code); + for (size_t i = 0; i < desc->ParameterCount(); ++i) { + params[i + 1] = Constant(caller, desc->GetParameterType(i), inputs[i]); + } + Node* call = caller.AddNode(caller.common()->Call(desc), + static_cast(desc->ParameterCount() + 1), + params.get()); + Node* ret = Constant(caller, MachineType::Int32(), 0); + for (size_t i = 0; i < desc->ReturnCount(); ++i) { + // Skip roughly one third of the outputs. + if (input.NextInt8(3) == 0) continue; + Node* ret_i = (desc->ReturnCount() == 1) + ? call + : caller.AddNode(caller.common()->Projection(i), call); + ret = caller.Int32Add(ret, ToInt32(caller, desc->GetReturnType(i), ret_i)); + expect += outputs[i]; + } + caller.Return(ret); + + // Call the wrapper. + CompilationInfo wrapper_info(ArrayVector("wrapper"), &zone, Code::STUB); + Handle wrapper_code = Pipeline::GenerateCodeForTesting( + &wrapper_info, i_isolate, wrapper_desc, caller.graph(), caller.Export()); + auto fn = GeneratedCode::FromCode(*wrapper_code); + int result = fn.Call(); + + CHECK_EQ(expect, result); + return 0; +} + +} // namespace fuzzer +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/test/fuzzer/multi_return/README.md b/test/fuzzer/multi_return/README.md new file mode 100644 index 0000000000..a3764e8a7c --- /dev/null +++ b/test/fuzzer/multi_return/README.md @@ -0,0 +1,4 @@ +All files in this directory are used by the trybots to check that the fuzzer +executes correctly, see +https://github.com/v8/v8/blob/master/test/fuzzer/README.md. There should be at +least one file in this directory, e.g. this README file. diff --git a/test/fuzzer/testcfg.py b/test/fuzzer/testcfg.py index 30f9d03fad..8ff2f0b104 100644 --- a/test/fuzzer/testcfg.py +++ b/test/fuzzer/testcfg.py @@ -26,11 +26,11 @@ class VariantsGenerator(testsuite.VariantsGenerator): class TestSuite(testsuite.TestSuite): - SUB_TESTS = ( 'json', 'parser', 'regexp', 'wasm', 'wasm_async', - 'wasm_call', 'wasm_code', 'wasm_compile', 'wasm_data_section', - 'wasm_function_sigs_section', 'wasm_globals_section', - 'wasm_imports_section', 'wasm_memory_section', 'wasm_names_section', - 'wasm_types_section' ) + SUB_TESTS = ( 'json', 'parser', 'regexp', 'multi_return', 'wasm', + 'wasm_async', 'wasm_call', 'wasm_code', 'wasm_compile', + 'wasm_data_section', 'wasm_function_sigs_section', + 'wasm_globals_section', 'wasm_imports_section', 'wasm_memory_section', + 'wasm_names_section', 'wasm_types_section' ) def ListTests(self, context): tests = []