LCOV - code coverage report
Current view: top level - test/fuzzer - multi-return.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 78 102 76.5 %
Date: 2019-04-17 Functions: 6 8 75.0 %

          Line data    Source code
       1             : // Copyright 2018 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include <cstddef>
       6             : #include <cstdint>
       7             : 
       8             : #include "src/compiler/backend/instruction-selector.h"
       9             : #include "src/compiler/graph.h"
      10             : #include "src/compiler/linkage.h"
      11             : #include "src/compiler/node.h"
      12             : #include "src/compiler/operator.h"
      13             : #include "src/compiler/pipeline.h"
      14             : #include "src/compiler/raw-machine-assembler.h"
      15             : #include "src/compiler/wasm-compiler.h"
      16             : #include "src/machine-type.h"
      17             : #include "src/objects-inl.h"
      18             : #include "src/objects.h"
      19             : #include "src/optimized-compilation-info.h"
      20             : #include "src/simulator.h"
      21             : #include "src/wasm/wasm-engine.h"
      22             : #include "src/wasm/wasm-features.h"
      23             : #include "src/wasm/wasm-limits.h"
      24             : #include "src/wasm/wasm-objects-inl.h"
      25             : #include "src/wasm/wasm-objects.h"
      26             : #include "src/wasm/wasm-opcodes.h"
      27             : #include "src/zone/accounting-allocator.h"
      28             : #include "src/zone/zone.h"
      29             : #include "test/fuzzer/fuzzer-support.h"
      30             : 
      31             : namespace v8 {
      32             : namespace internal {
      33             : namespace compiler {
      34             : namespace fuzzer {
      35             : 
      36             : constexpr MachineType kTypes[] = {
      37             :     // The first entry is just a placeholder, because '0' is a separator.
      38             :     MachineType(),
      39             : #if !V8_TARGET_ARCH_32_BIT
      40             :     MachineType::Int64(),
      41             : #endif
      42             :     MachineType::Int32(), MachineType::Float32(), MachineType::Float64()};
      43             : 
      44             : static constexpr int kNumTypes = arraysize(kTypes);
      45             : 
      46             : class InputProvider {
      47             :  public:
      48             :   InputProvider(const uint8_t* data, size_t size)
      49           1 :       : current_(data), end_(data + size) {}
      50             : 
      51             :   size_t NumNonZeroBytes(size_t offset, int limit) {
      52             :     DCHECK_LE(limit, std::numeric_limits<uint8_t>::max());
      53             :     DCHECK_GE(current_ + offset, current_);
      54             :     const uint8_t* p;
      55           6 :     for (p = current_ + offset; p < end_; ++p) {
      56           6 :       if (*p % limit == 0) break;
      57             :     }
      58           2 :     return p - current_ - offset;
      59             :   }
      60             : 
      61             :   int NextInt8(int limit) {
      62             :     DCHECK_LE(limit, std::numeric_limits<uint8_t>::max());
      63           9 :     if (current_ == end_) return 0;
      64           8 :     uint8_t result = *current_;
      65           9 :     current_++;
      66           8 :     return static_cast<int>(result) % limit;
      67             :   }
      68             : 
      69             :   int NextInt32(int limit) {
      70           0 :     if (current_ + sizeof(uint32_t) > end_) return 0;
      71             :     int result =
      72             :         ReadLittleEndianValue<int>(reinterpret_cast<Address>(current_));
      73           0 :     current_ += sizeof(uint32_t);
      74           0 :     return result % limit;
      75             :   }
      76             : 
      77             :  private:
      78             :   const uint8_t* current_;
      79             :   const uint8_t* end_;
      80             : };
      81             : 
      82           0 : MachineType RandomType(InputProvider* input) {
      83           4 :   return kTypes[input->NextInt8(kNumTypes)];
      84             : }
      85             : 
      86           4 : int index(MachineType type) { return static_cast<int>(type.representation()); }
      87             : 
      88           5 : Node* Constant(RawMachineAssembler& m, MachineType type, int value) {
      89           5 :   switch (type.representation()) {
      90             :     case MachineRepresentation::kWord32:
      91           3 :       return m.Int32Constant(static_cast<int32_t>(value));
      92             :     case MachineRepresentation::kWord64:
      93           0 :       return m.Int64Constant(static_cast<int64_t>(value));
      94             :     case MachineRepresentation::kFloat32:
      95           2 :       return m.Float32Constant(static_cast<float>(value));
      96             :     case MachineRepresentation::kFloat64:
      97           0 :       return m.Float64Constant(static_cast<double>(value));
      98             :     default:
      99           0 :       UNREACHABLE();
     100             :   }
     101             : }
     102             : 
     103           2 : Node* ToInt32(RawMachineAssembler& m, MachineType type, Node* a) {
     104           2 :   switch (type.representation()) {
     105             :     case MachineRepresentation::kWord32:
     106             :       return a;
     107             :     case MachineRepresentation::kWord64:
     108           0 :       return m.TruncateInt64ToInt32(a);
     109             :     case MachineRepresentation::kFloat32:
     110           0 :       return m.TruncateFloat32ToInt32(a);
     111             :     case MachineRepresentation::kFloat64:
     112           0 :       return m.RoundFloat64ToInt32(a);
     113             :     default:
     114           0 :       UNREACHABLE();
     115             :   }
     116             : }
     117             : 
     118           1 : CallDescriptor* CreateRandomCallDescriptor(Zone* zone, size_t return_count,
     119             :                                            size_t param_count,
     120             :                                            InputProvider* input) {
     121             :   wasm::FunctionSig::Builder builder(zone, return_count, param_count);
     122           1 :   for (size_t i = 0; i < param_count; i++) {
     123             :     MachineType type = RandomType(input);
     124           0 :     builder.AddParam(wasm::ValueTypes::ValueTypeFor(type));
     125             :   }
     126             :   // Read the end byte of the parameters.
     127             :   input->NextInt8(1);
     128             : 
     129           9 :   for (size_t i = 0; i < return_count; i++) {
     130             :     MachineType type = RandomType(input);
     131           4 :     builder.AddReturn(wasm::ValueTypes::ValueTypeFor(type));
     132             :   }
     133             : 
     134           1 :   return compiler::GetWasmCallDescriptor(zone, builder.Build());
     135             : }
     136             : 
     137           1 : std::shared_ptr<wasm::NativeModule> AllocateNativeModule(i::Isolate* isolate,
     138             :                                                          size_t code_size) {
     139           2 :   std::shared_ptr<wasm::WasmModule> module(new wasm::WasmModule);
     140           1 :   module->num_declared_functions = 1;
     141             : 
     142             :   // We have to add the code object to a NativeModule, because the
     143             :   // WasmCallDescriptor assumes that code is on the native heap and not
     144             :   // within a code object.
     145             :   return isolate->wasm_engine()->NewNativeModule(
     146           2 :       isolate, i::wasm::kAllWasmFeatures, code_size, false, std::move(module));
     147             : }
     148             : 
     149           1 : extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     150           1 :   v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
     151             :   v8::Isolate* isolate = support->GetIsolate();
     152             :   i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
     153             :   v8::Isolate::Scope isolate_scope(isolate);
     154           2 :   v8::HandleScope handle_scope(isolate);
     155           1 :   v8::Context::Scope context_scope(support->GetContext());
     156           2 :   v8::TryCatch try_catch(isolate);
     157           2 :   v8::internal::AccountingAllocator allocator;
     158           2 :   Zone zone(&allocator, ZONE_NAME);
     159             : 
     160             :   InputProvider input(data, size);
     161             :   // Create randomized descriptor.
     162             :   size_t param_count = input.NumNonZeroBytes(0, kNumTypes);
     163           1 :   if (param_count > Code::kMaxArguments) return 0;
     164             : 
     165           1 :   size_t return_count = input.NumNonZeroBytes(param_count + 1, kNumTypes);
     166           1 :   if (return_count > wasm::kV8MaxWasmFunctionMultiReturns) return 0;
     167             : 
     168             :   CallDescriptor* desc =
     169           1 :       CreateRandomCallDescriptor(&zone, return_count, param_count, &input);
     170             : 
     171           1 :   if (FLAG_wasm_fuzzer_gen_test) {
     172             :     // Print some debugging output which describes the produced signature.
     173             :     printf("[");
     174           0 :     for (size_t j = 0; j < param_count; ++j) {
     175             :       // Parameter 0 is the WasmContext.
     176           0 :       printf(" %s", MachineReprToString(
     177           0 :                         desc->GetParameterType(j + 1).representation()));
     178             :     }
     179             :     printf(" ] -> [");
     180           0 :     for (size_t j = 0; j < desc->ReturnCount(); ++j) {
     181           0 :       printf(" %s",
     182             :              MachineReprToString(desc->GetReturnType(j).representation()));
     183             :     }
     184             :     printf(" ]\n\n");
     185             :   }
     186             : 
     187             :   // Count parameters of each type.
     188             :   constexpr size_t kNumMachineRepresentations =
     189             :       static_cast<size_t>(MachineRepresentation::kLastRepresentation) + 1;
     190             : 
     191             :   // Trivial hash table for the number of occurrences of parameter types. The
     192             :   // MachineRepresentation of the parameter types is used as hash code.
     193           1 :   int counts[kNumMachineRepresentations] = {0};
     194           1 :   for (size_t i = 0; i < param_count; ++i) {
     195             :     // Parameter 0 is the WasmContext.
     196           0 :     ++counts[index(desc->GetParameterType(i + 1))];
     197             :   }
     198             : 
     199             :   // Generate random inputs.
     200           1 :   std::unique_ptr<int[]> inputs(new int[param_count]);
     201           1 :   std::unique_ptr<int[]> outputs(new int[desc->ReturnCount()]);
     202           1 :   for (size_t i = 0; i < param_count; ++i) {
     203           0 :     inputs[i] = input.NextInt32(10000);
     204             :   }
     205             : 
     206             :   RawMachineAssembler callee(
     207           1 :       i_isolate, new (&zone) Graph(&zone), desc,
     208             :       MachineType::PointerRepresentation(),
     209           2 :       InstructionSelector::SupportedMachineOperatorFlags());
     210             : 
     211             :   // Generate callee, returning random picks of its parameters.
     212           1 :   std::unique_ptr<Node* []> params(new Node*[desc->ParameterCount() + 2]);
     213             :   // The first input of a return is the number of stack slots that should be
     214             :   // popped before returning.
     215           1 :   std::unique_ptr<Node* []> returns(new Node*[desc->ReturnCount() + 1]);
     216           1 :   for (size_t i = 0; i < param_count; ++i) {
     217             :     // Parameter(0) is the WasmContext.
     218           0 :     params[i] = callee.Parameter(i + 1);
     219             :   }
     220             : 
     221           9 :   for (size_t i = 0; i < desc->ReturnCount(); ++i) {
     222             :     MachineType type = desc->GetReturnType(i);
     223             :     // Find a random same-type parameter to return. Use a constant if none.
     224           4 :     if (counts[index(type)] == 0) {
     225           4 :       returns[i] = Constant(callee, type, 42);
     226           4 :       outputs[i] = 42;
     227             :     } else {
     228             :       int n = input.NextInt32(counts[index(type)]);
     229             :       int k = 0;
     230           0 :       while (desc->GetParameterType(k + 1) != desc->GetReturnType(i) ||
     231             :              --n > 0) {
     232             :         ++k;
     233             :       }
     234           0 :       returns[i] = params[k];
     235           0 :       outputs[i] = inputs[k];
     236             :     }
     237             :   }
     238           1 :   callee.Return(static_cast<int>(desc->ReturnCount()), returns.get());
     239             : 
     240           2 :   OptimizedCompilationInfo info(ArrayVector("testing"), &zone, Code::STUB);
     241           2 :   Handle<Code> code = Pipeline::GenerateCodeForTesting(
     242             :                           &info, i_isolate, desc, callee.graph(),
     243           2 :                           AssemblerOptions::Default(i_isolate), callee.Export())
     244             :                           .ToHandleChecked();
     245             : 
     246             :   std::shared_ptr<wasm::NativeModule> module =
     247           1 :       AllocateNativeModule(i_isolate, code->raw_instruction_size());
     248           2 :   wasm::WasmCodeRefScope wasm_code_ref_scope;
     249           1 :   byte* code_start = module->AddCodeForTesting(code)->instructions().start();
     250             :   // Generate wrapper.
     251             :   int expect = 0;
     252             : 
     253             :   MachineSignature::Builder sig_builder(&zone, 1, 0);
     254             :   sig_builder.AddReturn(MachineType::Int32());
     255             : 
     256             :   CallDescriptor* wrapper_desc =
     257           1 :       Linkage::GetSimplifiedCDescriptor(&zone, sig_builder.Build());
     258             :   RawMachineAssembler caller(
     259           1 :       i_isolate, new (&zone) Graph(&zone), wrapper_desc,
     260             :       MachineType::PointerRepresentation(),
     261           2 :       InstructionSelector::SupportedMachineOperatorFlags());
     262             : 
     263           1 :   params[0] = caller.PointerConstant(code_start);
     264             :   // WasmContext dummy.
     265           1 :   params[1] = caller.PointerConstant(nullptr);
     266           1 :   for (size_t i = 0; i < param_count; ++i) {
     267           0 :     params[i + 2] = Constant(caller, desc->GetParameterType(i + 1), inputs[i]);
     268             :   }
     269           1 :   Node* call = caller.AddNode(caller.common()->Call(desc),
     270           1 :                               static_cast<int>(param_count + 2), params.get());
     271           1 :   Node* ret = Constant(caller, MachineType::Int32(), 0);
     272           9 :   for (size_t i = 0; i < desc->ReturnCount(); ++i) {
     273             :     // Skip roughly one third of the outputs.
     274           4 :     if (input.NextInt8(3) == 0) continue;
     275             :     Node* ret_i = (desc->ReturnCount() == 1)
     276             :                       ? call
     277           2 :                       : caller.AddNode(caller.common()->Projection(i), call);
     278           2 :     ret = caller.Int32Add(ret, ToInt32(caller, desc->GetReturnType(i), ret_i));
     279           2 :     expect += outputs[i];
     280             :   }
     281           1 :   caller.Return(ret);
     282             : 
     283             :   // Call the wrapper.
     284             :   OptimizedCompilationInfo wrapper_info(ArrayVector("wrapper"), &zone,
     285           2 :                                         Code::STUB);
     286             :   Handle<Code> wrapper_code =
     287           2 :       Pipeline::GenerateCodeForTesting(
     288             :           &wrapper_info, i_isolate, wrapper_desc, caller.graph(),
     289           2 :           AssemblerOptions::Default(i_isolate), caller.Export())
     290             :           .ToHandleChecked();
     291             : 
     292             :   auto fn = GeneratedCode<int32_t>::FromCode(*wrapper_code);
     293             :   int result = fn.Call();
     294             : 
     295           1 :   CHECK_EQ(expect, result);
     296             :   return 0;
     297             : }
     298             : 
     299             : }  // namespace fuzzer
     300             : }  // namespace compiler
     301             : }  // namespace internal
     302           2 : }  // namespace v8

Generated by: LCOV version 1.10