[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/859768 Reviewed-by: Ben Titzer <titzer@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Commit-Queue: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#50532}
This commit is contained in:
parent
c6c2d9a3e4
commit
a7b26c6b83
19
BUILD.gn
19
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",
|
||||
|
@ -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/',
|
||||
|
341
test/fuzzer/multi-return.cc
Normal file
341
test/fuzzer/multi-return.cc
Normal file
@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#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<uint8_t>::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<uint8_t>::max());
|
||||
if (current_ == end_) return 0;
|
||||
uint8_t result = *current_;
|
||||
current_++;
|
||||
return static_cast<int>(result) % limit;
|
||||
}
|
||||
|
||||
int NextInt32(int limit) {
|
||||
if (current_ + sizeof(uint32_t) > end_) return 0;
|
||||
int result = ReadLittleEndianValue<int>(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<int>(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<int32_t>(value));
|
||||
case MachineRepresentation::kWord64:
|
||||
return m.Int64Constant(static_cast<int64_t>(value));
|
||||
case MachineRepresentation::kFloat32:
|
||||
return m.Float32Constant(static_cast<float>(value));
|
||||
case MachineRepresentation::kFloat64:
|
||||
return m.Float64Constant(static_cast<double>(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*>(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<size_t>(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<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] = 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<Node* []> params(new Node*[desc->ParameterCount() + 1]);
|
||||
std::unique_ptr<Node* []> 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<int>(desc->ReturnCount()), returns.get());
|
||||
|
||||
CompilationInfo info(ArrayVector("testing"), &zone, Code::STUB);
|
||||
Handle<Code> 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<int>(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<Code> wrapper_code = Pipeline::GenerateCodeForTesting(
|
||||
&wrapper_info, i_isolate, wrapper_desc, caller.graph(), caller.Export());
|
||||
auto fn = GeneratedCode<int32_t>::FromCode(*wrapper_code);
|
||||
int result = fn.Call();
|
||||
|
||||
CHECK_EQ(expect, result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
4
test/fuzzer/multi_return/README.md
Normal file
4
test/fuzzer/multi_return/README.md
Normal file
@ -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.
|
@ -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 = []
|
||||
|
Loading…
Reference in New Issue
Block a user