Commit a7b26c6b authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[turbofan] Add fuzzer to test different signatures for multi-returns

This CL makes a fuzzer out of the cctest
test-multiple-return/ReturnMultipleRandom. The fuzzer creates a
CallDescriptor with input parameters and returns, and a function which
maps input parameters to returns. The fuzzer then calls this function
with a wrapper which checks that the correct mapping happened.

R=clemensh@chromium.org

Change-Id: Ib89c4063638baae69540a44486d7b2e9d13f8c1f
Reviewed-on: https://chromium-review.googlesource.com/859768Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50532}
parent c6c2d9a3
......@@ -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",
......
......@@ -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.
......
......@@ -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 <typename T>
......
......@@ -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<Code> GenerateCodeForTesting(
V8_EXPORT_PRIVATE static Handle<Code> GenerateCodeForTesting(
CompilationInfo* info, Isolate* isolate, CallDescriptor* call_descriptor,
Graph* graph, Schedule* schedule = nullptr,
SourcePositionTable* source_positions = nullptr);
......
......@@ -39,7 +39,7 @@ static_assert(static_cast<int>(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,
......
......@@ -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);
......
......@@ -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);
......
......@@ -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
##############################################################################
......
......@@ -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<int>(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<size_t>(MachineRepresentation::kLastRepresentation) + 1;
std::unique_ptr<int[]> 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<int[]> inputs(new int[desc->ParameterCount()]);
std::unique_ptr<int[]> 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<Node_ptr[]> params(
new Node_ptr[desc->ParameterCount() + 1]);
std::unique_ptr<Node_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<int>(desc->ReturnCount()), returns.get());
CompilationInfo info(ArrayVector("testing"), handles.main_zone(),
Code::STUB);
Handle<Code> 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<int32_t> 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<int>(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<Code> 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);
......
......@@ -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',
......
......@@ -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/',
......
This diff is collapsed.
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.
......@@ -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 = []
......
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