LCOV - code coverage report
Current view: top level - test/cctest/compiler - test-multiple-return.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 158 163 96.9 %
Date: 2019-04-18 Functions: 24 24 100.0 %

          Line data    Source code
       1             : // Copyright 2014 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 <cmath>
       6             : #include <functional>
       7             : #include <limits>
       8             : #include <memory>
       9             : 
      10             : #include "src/assembler.h"
      11             : #include "src/base/bits.h"
      12             : #include "src/compiler.h"
      13             : #include "src/compiler/linkage.h"
      14             : #include "src/compiler/wasm-compiler.h"
      15             : #include "src/machine-type.h"
      16             : #include "src/macro-assembler.h"
      17             : #include "src/objects-inl.h"
      18             : #include "src/wasm/function-compiler.h"
      19             : #include "src/wasm/wasm-engine.h"
      20             : #include "src/wasm/wasm-objects-inl.h"
      21             : #include "src/wasm/wasm-opcodes.h"
      22             : #include "test/cctest/cctest.h"
      23             : #include "test/cctest/compiler/codegen-tester.h"
      24             : #include "test/cctest/compiler/value-helper.h"
      25             : 
      26             : namespace v8 {
      27             : namespace internal {
      28             : namespace compiler {
      29             : 
      30             : namespace {
      31             : 
      32         752 : CallDescriptor* CreateCallDescriptor(Zone* zone, int return_count,
      33             :                                      int param_count, MachineType type) {
      34         752 :   wasm::FunctionSig::Builder builder(zone, return_count, param_count);
      35             : 
      36        7792 :   for (int i = 0; i < param_count; i++) {
      37        3520 :     builder.AddParam(wasm::ValueTypes::ValueTypeFor(type));
      38             :   }
      39             : 
      40       32944 :   for (int i = 0; i < return_count; i++) {
      41       16096 :     builder.AddReturn(wasm::ValueTypes::ValueTypeFor(type));
      42             :   }
      43         752 :   return compiler::GetWasmCallDescriptor(zone, builder.Build());
      44             : }
      45             : 
      46       14176 : Node* MakeConstant(RawMachineAssembler& m, MachineType type, int value) {
      47       14176 :   switch (type.representation()) {
      48             :     case MachineRepresentation::kWord32:
      49        3544 :       return m.Int32Constant(static_cast<int32_t>(value));
      50             :     case MachineRepresentation::kWord64:
      51        3544 :       return m.Int64Constant(static_cast<int64_t>(value));
      52             :     case MachineRepresentation::kFloat32:
      53        3544 :       return m.Float32Constant(static_cast<float>(value));
      54             :     case MachineRepresentation::kFloat64:
      55        3544 :       return m.Float64Constant(static_cast<double>(value));
      56             :     default:
      57           0 :       UNREACHABLE();
      58             :   }
      59             : }
      60             : 
      61        5024 : Node* Add(RawMachineAssembler& m, MachineType type, Node* a, Node* b) {
      62        5024 :   switch (type.representation()) {
      63             :     case MachineRepresentation::kWord32:
      64        1256 :       return m.Int32Add(a, b);
      65             :     case MachineRepresentation::kWord64:
      66        1256 :       return m.Int64Add(a, b);
      67             :     case MachineRepresentation::kFloat32:
      68        1256 :       return m.Float32Add(a, b);
      69             :     case MachineRepresentation::kFloat64:
      70        1256 :       return m.Float64Add(a, b);
      71             :     default:
      72           0 :       UNREACHABLE();
      73             :   }
      74             : }
      75             : 
      76        5312 : Node* Sub(RawMachineAssembler& m, MachineType type, Node* a, Node* b) {
      77        5312 :   switch (type.representation()) {
      78             :     case MachineRepresentation::kWord32:
      79        1328 :       return m.Int32Sub(a, b);
      80             :     case MachineRepresentation::kWord64:
      81        1328 :       return m.Int64Sub(a, b);
      82             :     case MachineRepresentation::kFloat32:
      83        1328 :       return m.Float32Sub(a, b);
      84             :     case MachineRepresentation::kFloat64:
      85        1328 :       return m.Float64Sub(a, b);
      86             :     default:
      87           0 :       UNREACHABLE();
      88             :   }
      89             : }
      90             : 
      91        1824 : Node* Mul(RawMachineAssembler& m, MachineType type, Node* a, Node* b) {
      92        1824 :   switch (type.representation()) {
      93             :     case MachineRepresentation::kWord32:
      94         456 :       return m.Int32Mul(a, b);
      95             :     case MachineRepresentation::kWord64:
      96         456 :       return m.Int64Mul(a, b);
      97             :     case MachineRepresentation::kFloat32:
      98         456 :       return m.Float32Mul(a, b);
      99             :     case MachineRepresentation::kFloat64:
     100         456 :       return m.Float64Mul(a, b);
     101             :     default:
     102           0 :       UNREACHABLE();
     103             :   }
     104             : }
     105             : 
     106         896 : Node* ToInt32(RawMachineAssembler& m, MachineType type, Node* a) {
     107         896 :   switch (type.representation()) {
     108             :     case MachineRepresentation::kWord32:
     109             :       return a;
     110             :     case MachineRepresentation::kWord64:
     111         224 :       return m.TruncateInt64ToInt32(a);
     112             :     case MachineRepresentation::kFloat32:
     113         224 :       return m.TruncateFloat32ToInt32(a);
     114             :     case MachineRepresentation::kFloat64:
     115         224 :       return m.RoundFloat64ToInt32(a);
     116             :     default:
     117           0 :       UNREACHABLE();
     118             :   }
     119             : }
     120             : 
     121         752 : std::shared_ptr<wasm::NativeModule> AllocateNativeModule(Isolate* isolate,
     122             :                                                          size_t code_size) {
     123        1504 :   std::shared_ptr<wasm::WasmModule> module(new wasm::WasmModule());
     124         752 :   module->num_declared_functions = 1;
     125             :   // We have to add the code object to a NativeModule, because the
     126             :   // WasmCallDescriptor assumes that code is on the native heap and not
     127             :   // within a code object.
     128             :   return isolate->wasm_engine()->NewNativeModule(
     129        1504 :       isolate, wasm::kAllWasmFeatures, code_size, false, std::move(module));
     130             : }
     131             : 
     132          16 : void TestReturnMultipleValues(MachineType type) {
     133             :   const int kMaxCount = 20;
     134             :   const int kMaxParamCount = 9;
     135             :   // Use 9 parameters as a regression test or https://crbug.com/838098.
     136          80 :   for (int param_count : {2, kMaxParamCount}) {
     137        1312 :     for (int count = 0; count < kMaxCount; ++count) {
     138         640 :       printf("\n==== type = %s, count = %d ====\n\n\n",
     139             :              MachineReprToString(type.representation()), count);
     140        1280 :       v8::internal::AccountingAllocator allocator;
     141        1280 :       Zone zone(&allocator, ZONE_NAME);
     142             :       CallDescriptor* desc =
     143         640 :           CreateCallDescriptor(&zone, count, param_count, type);
     144        1280 :       HandleAndZoneScope handles;
     145             :       RawMachineAssembler m(
     146             :           handles.main_isolate(),
     147         640 :           new (handles.main_zone()) Graph(handles.main_zone()), desc,
     148             :           MachineType::PointerRepresentation(),
     149        1280 :           InstructionSelector::SupportedMachineOperatorFlags());
     150             : 
     151             :       // m.Parameter(0) is the WasmContext.
     152         640 :       Node* p0 = m.Parameter(1);
     153         640 :       Node* p1 = m.Parameter(2);
     154             :       typedef Node* Node_ptr;
     155         640 :       std::unique_ptr<Node_ptr[]> returns(new Node_ptr[count]);
     156       12800 :       for (int i = 0; i < count; ++i) {
     157        8320 :         if (i % 3 == 0) returns[i] = Add(m, type, p0, p1);
     158        8096 :         if (i % 3 == 1) returns[i] = Sub(m, type, p0, p1);
     159        7904 :         if (i % 3 == 2) returns[i] = Mul(m, type, p0, p1);
     160             :       }
     161         640 :       m.Return(count, returns.get());
     162             : 
     163             :       OptimizedCompilationInfo info(ArrayVector("testing"), handles.main_zone(),
     164        1280 :                                     Code::WASM_FUNCTION);
     165             :       Handle<Code> code =
     166        1280 :           Pipeline::GenerateCodeForTesting(
     167             :               &info, handles.main_isolate(), desc, m.graph(),
     168        1280 :               AssemblerOptions::Default(handles.main_isolate()), m.Export())
     169             :               .ToHandleChecked();
     170             : #ifdef ENABLE_DISASSEMBLER
     171             :       if (FLAG_print_code) {
     172             :         StdoutStream os;
     173             :         code->Disassemble("multi_value", os);
     174             :       }
     175             : #endif
     176             : 
     177             :       const int a = 47, b = 12;
     178             :       int expect = 0;
     179       12800 :       for (int i = 0, sign = +1; i < count; ++i) {
     180        6080 :         if (i % 3 == 0) expect += sign * (a + b);
     181        6080 :         if (i % 3 == 1) expect += sign * (a - b);
     182        6080 :         if (i % 3 == 2) expect += sign * (a * b);
     183        6080 :         if (i % 4 == 0) sign = -sign;
     184             :       }
     185             : 
     186             :       std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
     187         640 :           handles.main_isolate(), code->raw_instruction_size());
     188        1280 :       wasm::WasmCodeRefScope wasm_code_ref_scope;
     189             :       byte* code_start =
     190         640 :           module->AddCodeForTesting(code)->instructions().start();
     191             : 
     192         640 :       RawMachineAssemblerTester<int32_t> mt(Code::Kind::JS_TO_WASM_FUNCTION);
     193         640 :       const int input_count = 2 + param_count;
     194             :       Node* call_inputs[2 + kMaxParamCount];
     195         640 :       call_inputs[0] = mt.PointerConstant(code_start);
     196             :       // WasmContext dummy
     197         640 :       call_inputs[1] = mt.PointerConstant(nullptr);
     198             :       // Special inputs for the test.
     199         640 :       call_inputs[2] = MakeConstant(mt, type, a);
     200         640 :       call_inputs[3] = MakeConstant(mt, type, b);
     201        5120 :       for (int i = 2; i < param_count; i++) {
     202        2240 :         call_inputs[2 + i] = MakeConstant(mt, type, i);
     203             :       }
     204             : 
     205         640 :       Node* ret_multi = mt.AddNode(mt.common()->Call(desc),
     206         640 :                                    input_count, call_inputs);
     207         640 :       Node* ret = MakeConstant(mt, type, 0);
     208             :       bool sign = false;
     209       12800 :       for (int i = 0; i < count; ++i) {
     210             :         Node* x = (count == 1)
     211             :                       ? ret_multi
     212        6080 :                       : mt.AddNode(mt.common()->Projection(i), ret_multi);
     213        6080 :         ret = sign ? Sub(mt, type, ret, x) : Add(mt, type, ret, x);
     214        6080 :         if (i % 4 == 0) sign = !sign;
     215             :       }
     216         640 :       mt.Return(ToInt32(mt, type, ret));
     217             : #ifdef ENABLE_DISASSEMBLER
     218             :       Handle<Code> code2 = mt.GetCode();
     219             :       if (FLAG_print_code) {
     220             :         StdoutStream os;
     221             :         code2->Disassemble("multi_value_call", os);
     222             :       }
     223             : #endif
     224         640 :       CHECK_EQ(expect, mt.Call());
     225             :     }
     226             :   }
     227          16 : }
     228             : 
     229             : }  // namespace
     230             : 
     231             : #define TEST_MULTI(Type, type) \
     232             :   TEST(ReturnMultiple##Type) { TestReturnMultipleValues(type); }
     233             : 
     234       26660 : TEST_MULTI(Int32, MachineType::Int32())
     235             : #if (!V8_TARGET_ARCH_32_BIT)
     236       26660 : TEST_MULTI(Int64, MachineType::Int64())
     237             : #endif
     238       26660 : TEST_MULTI(Float32, MachineType::Float32())
     239       26660 : TEST_MULTI(Float64, MachineType::Float64())
     240             : 
     241             : #undef TEST_MULTI
     242             : 
     243          16 : void ReturnLastValue(MachineType type) {
     244          16 :   int slot_counts[] = {1, 2, 3, 600};
     245         144 :   for (auto slot_count : slot_counts) {
     246         128 :     v8::internal::AccountingAllocator allocator;
     247         128 :     Zone zone(&allocator, ZONE_NAME);
     248             :     // The wasm-linkage provides 2 return registers at the moment, on all
     249             :     // platforms.
     250          64 :     const int return_count = 2 + slot_count;
     251             : 
     252          64 :     CallDescriptor* desc = CreateCallDescriptor(&zone, return_count, 0, type);
     253             : 
     254         128 :     HandleAndZoneScope handles;
     255             :     RawMachineAssembler m(handles.main_isolate(),
     256          64 :                           new (handles.main_zone()) Graph(handles.main_zone()),
     257             :                           desc, MachineType::PointerRepresentation(),
     258         128 :                           InstructionSelector::SupportedMachineOperatorFlags());
     259             : 
     260          64 :     std::unique_ptr<Node* []> returns(new Node*[return_count]);
     261             : 
     262       19712 :     for (int i = 0; i < return_count; ++i) {
     263       19648 :       returns[i] = MakeConstant(m, type, i);
     264             :     }
     265             : 
     266          64 :     m.Return(return_count, returns.get());
     267             : 
     268             :     OptimizedCompilationInfo info(ArrayVector("testing"), handles.main_zone(),
     269         128 :                                   Code::WASM_FUNCTION);
     270             :     Handle<Code> code =
     271         128 :         Pipeline::GenerateCodeForTesting(
     272             :             &info, handles.main_isolate(), desc, m.graph(),
     273         128 :             AssemblerOptions::Default(handles.main_isolate()), m.Export())
     274             :             .ToHandleChecked();
     275             : 
     276             :     std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
     277          64 :         handles.main_isolate(), code->raw_instruction_size());
     278         128 :     wasm::WasmCodeRefScope wasm_code_ref_scope;
     279          64 :     byte* code_start = module->AddCodeForTesting(code)->instructions().start();
     280             : 
     281             :     // Generate caller.
     282          64 :     int expect = return_count - 1;
     283          64 :     RawMachineAssemblerTester<int32_t> mt;
     284             :     Node* inputs[] = {mt.PointerConstant(code_start),
     285             :                       // WasmContext dummy
     286         128 :                       mt.PointerConstant(nullptr)};
     287             : 
     288          64 :     Node* call = mt.AddNode(mt.common()->Call(desc), 2, inputs);
     289             : 
     290         128 :     mt.Return(ToInt32(
     291          64 :         mt, type, mt.AddNode(mt.common()->Projection(return_count - 1), call)));
     292             : 
     293          64 :     CHECK_EQ(expect, mt.Call());
     294             :   }
     295          16 : }
     296             : 
     297       26660 : TEST(ReturnLastValueInt32) { ReturnLastValue(MachineType::Int32()); }
     298             : #if (!V8_TARGET_ARCH_32_BIT)
     299       26660 : TEST(ReturnLastValueInt64) { ReturnLastValue(MachineType::Int64()); }
     300             : #endif
     301       26660 : TEST(ReturnLastValueFloat32) { ReturnLastValue(MachineType::Float32()); }
     302       26660 : TEST(ReturnLastValueFloat64) { ReturnLastValue(MachineType::Float64()); }
     303             : 
     304          16 : void ReturnSumOfReturns(MachineType type) {
     305         112 :   for (int unused_stack_slots = 0; unused_stack_slots <= 2;
     306             :        ++unused_stack_slots) {
     307          96 :     v8::internal::AccountingAllocator allocator;
     308          96 :     Zone zone(&allocator, ZONE_NAME);
     309             :     // Let {unused_stack_slots + 1} returns be on the stack.
     310             :     // The wasm-linkage provides 2 return registers at the moment, on all
     311             :     // platforms.
     312          48 :     const int return_count = 2 + unused_stack_slots + 1;
     313             : 
     314          48 :     CallDescriptor* desc = CreateCallDescriptor(&zone, return_count, 0, type);
     315             : 
     316          96 :     HandleAndZoneScope handles;
     317             :     RawMachineAssembler m(handles.main_isolate(),
     318          48 :                           new (handles.main_zone()) Graph(handles.main_zone()),
     319             :                           desc, MachineType::PointerRepresentation(),
     320          96 :                           InstructionSelector::SupportedMachineOperatorFlags());
     321             : 
     322          48 :     std::unique_ptr<Node* []> returns(new Node*[return_count]);
     323             : 
     324         432 :     for (int i = 0; i < return_count; ++i) {
     325         384 :       returns[i] = MakeConstant(m, type, i);
     326             :     }
     327             : 
     328          48 :     m.Return(return_count, returns.get());
     329             : 
     330             :     OptimizedCompilationInfo info(ArrayVector("testing"), handles.main_zone(),
     331          96 :                                   Code::WASM_FUNCTION);
     332             :     Handle<Code> code =
     333          96 :         Pipeline::GenerateCodeForTesting(
     334             :             &info, handles.main_isolate(), desc, m.graph(),
     335          96 :             AssemblerOptions::Default(handles.main_isolate()), m.Export())
     336             :             .ToHandleChecked();
     337             : 
     338             :     std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
     339          48 :         handles.main_isolate(), code->raw_instruction_size());
     340          96 :     wasm::WasmCodeRefScope wasm_code_ref_scope;
     341          48 :     byte* code_start = module->AddCodeForTesting(code)->instructions().start();
     342             : 
     343             :     // Generate caller.
     344          48 :     RawMachineAssemblerTester<int32_t> mt;
     345             :     Node* call_inputs[] = {mt.PointerConstant(code_start),
     346             :                            // WasmContext dummy
     347          96 :                            mt.PointerConstant(nullptr)};
     348             : 
     349          48 :     Node* call = mt.AddNode(mt.common()->Call(desc), 2, call_inputs);
     350             : 
     351             :     uint32_t expect = 0;
     352          48 :     Node* result = mt.Int32Constant(0);
     353             : 
     354         432 :     for (int i = 0; i < return_count; ++i) {
     355         192 :       expect += i;
     356         384 :       result = mt.Int32Add(
     357             :           result,
     358         192 :           ToInt32(mt, type, mt.AddNode(mt.common()->Projection(i), call)));
     359             :     }
     360             : 
     361          48 :     mt.Return(result);
     362             : 
     363          48 :     CHECK_EQ(expect, mt.Call());
     364             :   }
     365          16 : }
     366             : 
     367       26660 : TEST(ReturnSumOfReturnsInt32) { ReturnSumOfReturns(MachineType::Int32()); }
     368             : #if (!V8_TARGET_ARCH_32_BIT)
     369       26660 : TEST(ReturnSumOfReturnsInt64) { ReturnSumOfReturns(MachineType::Int64()); }
     370             : #endif
     371       26660 : TEST(ReturnSumOfReturnsFloat32) { ReturnSumOfReturns(MachineType::Float32()); }
     372       26660 : TEST(ReturnSumOfReturnsFloat64) { ReturnSumOfReturns(MachineType::Float64()); }
     373             : 
     374             : }  // namespace compiler
     375             : }  // namespace internal
     376       79968 : }  // namespace v8

Generated by: LCOV version 1.10