LCOV - code coverage report
Current view: top level - test/unittests/compiler - bytecode-analysis-unittest.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 240 242 99.2 %
Date: 2019-04-17 Functions: 26 36 72.2 %

          Line data    Source code
       1             : // Copyright 2015 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 "src/v8.h"
       6             : 
       7             : #include "src/compiler/bytecode-analysis.h"
       8             : #include "src/interpreter/bytecode-array-builder.h"
       9             : #include "src/interpreter/bytecode-array-iterator.h"
      10             : #include "src/interpreter/bytecode-decoder.h"
      11             : #include "src/interpreter/bytecode-label.h"
      12             : #include "src/interpreter/control-flow-builders.h"
      13             : #include "src/objects-inl.h"
      14             : #include "test/unittests/interpreter/bytecode-utils.h"
      15             : #include "test/unittests/test-utils.h"
      16             : 
      17             : namespace v8 {
      18             : namespace internal {
      19             : namespace compiler {
      20             : 
      21             : using ToBooleanMode = interpreter::BytecodeArrayBuilder::ToBooleanMode;
      22             : 
      23             : class BytecodeAnalysisTest : public TestWithIsolateAndZone {
      24             :  public:
      25          10 :   BytecodeAnalysisTest() = default;
      26          20 :   ~BytecodeAnalysisTest() override = default;
      27             : 
      28          10 :   static void SetUpTestCase() {
      29          10 :     CHECK_NULL(save_flags_);
      30          10 :     save_flags_ = new SaveFlags();
      31          10 :     i::FLAG_ignition_elide_noneffectful_bytecodes = false;
      32          10 :     i::FLAG_ignition_reo = false;
      33             : 
      34             :     TestWithIsolateAndZone::SetUpTestCase();
      35          10 :   }
      36             : 
      37          10 :   static void TearDownTestCase() {
      38             :     TestWithIsolateAndZone::TearDownTestCase();
      39          10 :     delete save_flags_;
      40          10 :     save_flags_ = nullptr;
      41          10 :   }
      42             : 
      43         124 :   std::string ToLivenessString(const BytecodeLivenessState* liveness) const {
      44             :     const BitVector& bit_vector = liveness->bit_vector();
      45             : 
      46             :     std::string out;
      47         124 :     out.resize(bit_vector.length());
      48        1116 :     for (int i = 0; i < bit_vector.length(); ++i) {
      49         496 :       if (bit_vector.Contains(i)) {
      50         382 :         out[i] = 'L';
      51             :       } else {
      52         610 :         out[i] = '.';
      53             :       }
      54             :     }
      55         124 :     return out;
      56             :   }
      57             : 
      58          10 :   void EnsureLivenessMatches(
      59             :       Handle<BytecodeArray> bytecode,
      60             :       const std::vector<std::pair<std::string, std::string>>&
      61             :           expected_liveness) {
      62          20 :     BytecodeAnalysis analysis(bytecode, zone(), true);
      63          10 :     analysis.Analyze(BailoutId::None());
      64             : 
      65          10 :     interpreter::BytecodeArrayIterator iterator(bytecode);
      66         134 :     for (auto liveness : expected_liveness) {
      67         124 :       std::stringstream ss;
      68          62 :       ss << std::setw(4) << iterator.current_offset() << " : ";
      69          62 :       iterator.PrintTo(ss);
      70             : 
      71         124 :       EXPECT_EQ(liveness.first, ToLivenessString(analysis.GetInLivenessFor(
      72             :                                     iterator.current_offset())))
      73           0 :           << " at bytecode " << ss.str();
      74             : 
      75         124 :       EXPECT_EQ(liveness.second, ToLivenessString(analysis.GetOutLivenessFor(
      76             :                                      iterator.current_offset())))
      77           0 :           << " at bytecode " << ss.str();
      78             : 
      79          62 :       iterator.Advance();
      80             :     }
      81             : 
      82          20 :     EXPECT_TRUE(iterator.done());
      83          10 :   }
      84             : 
      85             :  private:
      86             :   static SaveFlags* save_flags_;
      87             : 
      88             :   DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysisTest);
      89             : };
      90             : 
      91             : SaveFlags* BytecodeAnalysisTest::save_flags_ = nullptr;
      92             : 
      93       15444 : TEST_F(BytecodeAnalysisTest, EmptyBlock) {
      94           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
      95           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
      96             : 
      97             :   interpreter::Register reg_0(0);
      98             : 
      99           1 :   builder.Return();
     100           1 :   expected_liveness.emplace_back("...L", "....");
     101             : 
     102           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     103             : 
     104           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     105           1 : }
     106             : 
     107       15444 : TEST_F(BytecodeAnalysisTest, SimpleLoad) {
     108           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     109           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     110             : 
     111             :   interpreter::Register reg_0(0);
     112             : 
     113           1 :   builder.LoadAccumulatorWithRegister(reg_0);
     114           1 :   expected_liveness.emplace_back("L...", "...L");
     115             : 
     116           1 :   builder.Return();
     117           1 :   expected_liveness.emplace_back("...L", "....");
     118             : 
     119           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     120             : 
     121           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     122           1 : }
     123             : 
     124       15444 : TEST_F(BytecodeAnalysisTest, StoreThenLoad) {
     125           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     126           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     127             : 
     128             :   interpreter::Register reg_0(0);
     129             : 
     130           1 :   builder.StoreAccumulatorInRegister(reg_0);
     131           1 :   expected_liveness.emplace_back("...L", "L...");
     132             : 
     133           1 :   builder.LoadAccumulatorWithRegister(reg_0);
     134           1 :   expected_liveness.emplace_back("L...", "...L");
     135             : 
     136           1 :   builder.Return();
     137           1 :   expected_liveness.emplace_back("...L", "....");
     138             : 
     139           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     140             : 
     141           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     142           1 : }
     143             : 
     144       15444 : TEST_F(BytecodeAnalysisTest, DiamondLoad) {
     145           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     146           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     147             : 
     148             :   interpreter::Register reg_0(0);
     149             :   interpreter::Register reg_1(1);
     150             :   interpreter::Register reg_2(2);
     151             : 
     152             :   interpreter::BytecodeLabel ld1_label;
     153             :   interpreter::BytecodeLabel end_label;
     154             : 
     155           1 :   builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &ld1_label);
     156           1 :   expected_liveness.emplace_back("LLLL", "LLL.");
     157             : 
     158           1 :   builder.LoadAccumulatorWithRegister(reg_0);
     159           1 :   expected_liveness.emplace_back("L.L.", "..L.");
     160             : 
     161           1 :   builder.Jump(&end_label);
     162           1 :   expected_liveness.emplace_back("..L.", "..L.");
     163             : 
     164           1 :   builder.Bind(&ld1_label);
     165           1 :   builder.LoadAccumulatorWithRegister(reg_1);
     166           1 :   expected_liveness.emplace_back(".LL.", "..L.");
     167             : 
     168           1 :   builder.Bind(&end_label);
     169             : 
     170           1 :   builder.LoadAccumulatorWithRegister(reg_2);
     171           1 :   expected_liveness.emplace_back("..L.", "...L");
     172             : 
     173           1 :   builder.Return();
     174           1 :   expected_liveness.emplace_back("...L", "....");
     175             : 
     176           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     177             : 
     178           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     179           1 : }
     180             : 
     181       15444 : TEST_F(BytecodeAnalysisTest, DiamondLookupsAndBinds) {
     182           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     183           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     184             : 
     185             :   interpreter::Register reg_0(0);
     186             :   interpreter::Register reg_1(1);
     187             :   interpreter::Register reg_2(2);
     188             : 
     189             :   interpreter::BytecodeLabel ld1_label;
     190             :   interpreter::BytecodeLabel end_label;
     191             : 
     192           1 :   builder.StoreAccumulatorInRegister(reg_0);
     193           1 :   expected_liveness.emplace_back(".LLL", "LLLL");
     194             : 
     195           1 :   builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &ld1_label);
     196           1 :   expected_liveness.emplace_back("LLLL", "LLL.");
     197             : 
     198             :   {
     199           1 :     builder.LoadAccumulatorWithRegister(reg_0);
     200           1 :     expected_liveness.emplace_back("L...", "...L");
     201             : 
     202           1 :     builder.StoreAccumulatorInRegister(reg_2);
     203           1 :     expected_liveness.emplace_back("...L", "..L.");
     204             : 
     205           1 :     builder.Jump(&end_label);
     206           1 :     expected_liveness.emplace_back("..L.", "..L.");
     207             :   }
     208             : 
     209           1 :   builder.Bind(&ld1_label);
     210             :   {
     211           1 :     builder.LoadAccumulatorWithRegister(reg_1);
     212           1 :     expected_liveness.emplace_back(".LL.", "..L.");
     213             :   }
     214             : 
     215           1 :   builder.Bind(&end_label);
     216             : 
     217           1 :   builder.LoadAccumulatorWithRegister(reg_2);
     218           1 :   expected_liveness.emplace_back("..L.", "...L");
     219             : 
     220           1 :   builder.Return();
     221           1 :   expected_liveness.emplace_back("...L", "....");
     222             : 
     223           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     224             : 
     225           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     226           1 : }
     227             : 
     228       15444 : TEST_F(BytecodeAnalysisTest, SimpleLoop) {
     229           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     230           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     231             : 
     232             :   interpreter::Register reg_0(0);
     233             :   interpreter::Register reg_1(1);
     234             :   interpreter::Register reg_2(2);
     235             : 
     236             :   // Kill r0.
     237           1 :   builder.StoreAccumulatorInRegister(reg_0);
     238           1 :   expected_liveness.emplace_back("..LL", "L.L.");
     239             : 
     240             :   {
     241           1 :     interpreter::LoopBuilder loop_builder(&builder, nullptr, nullptr);
     242           1 :     loop_builder.LoopHeader();
     243             : 
     244           1 :     builder.LoadUndefined();
     245           1 :     expected_liveness.emplace_back("L.L.", "L.LL");
     246             : 
     247             :     builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean,
     248           1 :                        loop_builder.break_labels()->New());
     249           1 :     expected_liveness.emplace_back("L.LL", "L.L.");
     250             : 
     251             :     // Gen r0.
     252           1 :     builder.LoadAccumulatorWithRegister(reg_0);
     253           1 :     expected_liveness.emplace_back("L...", "L..L");
     254             : 
     255             :     // Kill r2.
     256           1 :     builder.StoreAccumulatorInRegister(reg_2);
     257           1 :     expected_liveness.emplace_back("L..L", "L.L.");
     258             : 
     259           1 :     loop_builder.BindContinueTarget();
     260           1 :     loop_builder.JumpToHeader(0);
     261           1 :     expected_liveness.emplace_back("L.L.", "L.L.");
     262             :   }
     263             : 
     264             :   // Gen r2.
     265           1 :   builder.LoadAccumulatorWithRegister(reg_2);
     266           1 :   expected_liveness.emplace_back("..L.", "...L");
     267             : 
     268           1 :   builder.Return();
     269           1 :   expected_liveness.emplace_back("...L", "....");
     270             : 
     271           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     272             : 
     273           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     274           1 : }
     275             : 
     276       15444 : TEST_F(BytecodeAnalysisTest, TryCatch) {
     277           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     278           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     279             : 
     280             :   interpreter::Register reg_0(0);
     281             :   interpreter::Register reg_1(1);
     282             :   interpreter::Register reg_context(2);
     283             : 
     284             :   // Kill r0.
     285           1 :   builder.StoreAccumulatorInRegister(reg_0);
     286           1 :   expected_liveness.emplace_back(".LLL", "LLL.");
     287             : 
     288             :   interpreter::TryCatchBuilder try_builder(&builder, nullptr, nullptr,
     289           1 :                                            HandlerTable::CAUGHT);
     290           1 :   try_builder.BeginTry(reg_context);
     291             :   {
     292             :     // Gen r0.
     293           1 :     builder.LoadAccumulatorWithRegister(reg_0);
     294           1 :     expected_liveness.emplace_back("LLL.", ".LLL");
     295             : 
     296             :     // Kill r0.
     297           1 :     builder.StoreAccumulatorInRegister(reg_0);
     298           1 :     expected_liveness.emplace_back(".LLL", ".LL.");
     299             : 
     300           1 :     builder.CallRuntime(Runtime::kThrow);
     301           1 :     expected_liveness.emplace_back(".LL.", ".LLL");
     302             : 
     303           1 :     builder.StoreAccumulatorInRegister(reg_0);
     304             :     // Star can't throw, so doesn't take handler liveness
     305           1 :     expected_liveness.emplace_back("...L", "...L");
     306             :   }
     307           1 :   try_builder.EndTry();
     308           1 :   expected_liveness.emplace_back("...L", "...L");
     309             : 
     310             :   // Catch
     311             :   {
     312           1 :     builder.LoadAccumulatorWithRegister(reg_1);
     313           1 :     expected_liveness.emplace_back(".L..", "...L");
     314             :   }
     315           1 :   try_builder.EndCatch();
     316             : 
     317           1 :   builder.Return();
     318           1 :   expected_liveness.emplace_back("...L", "....");
     319             : 
     320           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     321             : 
     322           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     323           1 : }
     324             : 
     325       15444 : TEST_F(BytecodeAnalysisTest, DiamondInLoop) {
     326             :   // For a logic diamond inside a loop, the liveness down one path of the
     327             :   // diamond should eventually propagate up the other path when the loop is
     328             :   // reprocessed.
     329             : 
     330           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     331           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     332             : 
     333             :   interpreter::Register reg_0(0);
     334             : 
     335             :   {
     336           1 :     interpreter::LoopBuilder loop_builder(&builder, nullptr, nullptr);
     337           1 :     loop_builder.LoopHeader();
     338             : 
     339           1 :     builder.LoadUndefined();
     340           1 :     expected_liveness.emplace_back("L...", "L..L");
     341             :     builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean,
     342           1 :                        loop_builder.break_labels()->New());
     343           1 :     expected_liveness.emplace_back("L..L", "L..L");
     344             : 
     345             :     interpreter::BytecodeLabel ld1_label;
     346             :     interpreter::BytecodeLabel end_label;
     347           1 :     builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &ld1_label);
     348           1 :     expected_liveness.emplace_back("L..L", "L...");
     349             : 
     350             :     {
     351           1 :       builder.Jump(&end_label);
     352           1 :       expected_liveness.emplace_back("L...", "L...");
     353             :     }
     354             : 
     355           1 :     builder.Bind(&ld1_label);
     356             :     {
     357             :       // Gen r0.
     358           1 :       builder.LoadAccumulatorWithRegister(reg_0);
     359           1 :       expected_liveness.emplace_back("L...", "L...");
     360             :     }
     361             : 
     362           1 :     builder.Bind(&end_label);
     363             : 
     364           1 :     loop_builder.BindContinueTarget();
     365           1 :     loop_builder.JumpToHeader(0);
     366           1 :     expected_liveness.emplace_back("L...", "L...");
     367             :   }
     368             : 
     369           1 :   builder.LoadUndefined();
     370           1 :   expected_liveness.emplace_back("....", "...L");
     371           1 :   builder.Return();
     372           1 :   expected_liveness.emplace_back("...L", "....");
     373             : 
     374           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     375             : 
     376           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     377           1 : }
     378             : 
     379       15444 : TEST_F(BytecodeAnalysisTest, KillingLoopInsideLoop) {
     380             :   // For a loop inside a loop, the inner loop has to be processed after the
     381             :   // outer loop has been processed, to ensure that it can propagate the
     382             :   // information in its header. Consider
     383             :   //
     384             :   //     0: do {
     385             :   //     1:   acc = r0;
     386             :   //     2:   acc = r1;
     387             :   //     3:   do {
     388             :   //     4:     r0 = acc;
     389             :   //     5:     break;
     390             :   //     6:   } while(true);
     391             :   //     7: } while(true);
     392             :   //
     393             :   // r0 should should be dead at 3 and 6, while r1 is live throughout. On the
     394             :   // initial pass, r1 is dead from 3-7. On the outer loop pass, it becomes live
     395             :   // in 3 and 7 (but not 4-6 because 6 only reads liveness from 3). Only after
     396             :   // the inner loop pass does it become live in 4-6. It's necessary, however, to
     397             :   // still process the inner loop when processing the outer loop, to ensure that
     398             :   // r1 becomes live in 3 (via 5), but r0 stays dead (because of 4).
     399             : 
     400           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     401           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     402             : 
     403             :   interpreter::Register reg_0(0);
     404             :   interpreter::Register reg_1(1);
     405             : 
     406             :   {
     407           1 :     interpreter::LoopBuilder loop_builder(&builder, nullptr, nullptr);
     408           1 :     loop_builder.LoopHeader();
     409             : 
     410             :     // Gen r0.
     411           1 :     builder.LoadAccumulatorWithRegister(reg_0);
     412           1 :     expected_liveness.emplace_back("LL..", ".L..");
     413             : 
     414             :     // Gen r1.
     415           1 :     builder.LoadAccumulatorWithRegister(reg_1);
     416           1 :     expected_liveness.emplace_back(".L..", ".L.L");
     417             : 
     418             :     builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean,
     419           1 :                        loop_builder.break_labels()->New());
     420           1 :     expected_liveness.emplace_back(".L.L", ".L..");
     421             : 
     422             :     {
     423           1 :       interpreter::LoopBuilder inner_loop_builder(&builder, nullptr, nullptr);
     424           1 :       inner_loop_builder.LoopHeader();
     425             : 
     426             :       // Kill r0.
     427           1 :       builder.LoadUndefined();
     428           1 :       expected_liveness.emplace_back(".L..", ".L.L");
     429           1 :       builder.StoreAccumulatorInRegister(reg_0);
     430           1 :       expected_liveness.emplace_back(".L.L", "LL.L");
     431             : 
     432             :       builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean,
     433           1 :                          inner_loop_builder.break_labels()->New());
     434           1 :       expected_liveness.emplace_back("LL.L", "LL..");
     435             : 
     436           1 :       inner_loop_builder.BindContinueTarget();
     437           1 :       inner_loop_builder.JumpToHeader(1);
     438           1 :       expected_liveness.emplace_back(".L..", ".L..");
     439             :     }
     440             : 
     441           1 :     loop_builder.BindContinueTarget();
     442           1 :     loop_builder.JumpToHeader(0);
     443           1 :     expected_liveness.emplace_back("LL..", "LL..");
     444             :   }
     445             : 
     446           1 :   builder.LoadUndefined();
     447           1 :   expected_liveness.emplace_back("....", "...L");
     448           1 :   builder.Return();
     449           1 :   expected_liveness.emplace_back("...L", "....");
     450             : 
     451           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     452             : 
     453           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     454           1 : }
     455             : 
     456       15444 : TEST_F(BytecodeAnalysisTest, SuspendPoint) {
     457           2 :   interpreter::BytecodeArrayBuilder builder(zone(), 3, 3);
     458           1 :   std::vector<std::pair<std::string, std::string>> expected_liveness;
     459             : 
     460             :   interpreter::Register reg_0(0);
     461             :   interpreter::Register reg_1(1);
     462             :   interpreter::Register reg_gen(2);
     463             :   interpreter::BytecodeJumpTable* gen_jump_table =
     464           1 :       builder.AllocateJumpTable(1, 0);
     465             : 
     466           1 :   builder.StoreAccumulatorInRegister(reg_gen);
     467           1 :   expected_liveness.emplace_back("L..L", "L.LL");
     468             : 
     469             :   // Note: technically, r0 should be dead here since the resume will write it,
     470             :   // but in practice the bytecode analysis doesn't bother to special case it,
     471             :   // since the generator switch is close to the top of the function anyway.
     472           1 :   builder.SwitchOnGeneratorState(reg_gen, gen_jump_table);
     473           1 :   expected_liveness.emplace_back("L.LL", "L.LL");
     474             : 
     475           1 :   builder.StoreAccumulatorInRegister(reg_0);
     476           1 :   expected_liveness.emplace_back("..LL", "L.LL");
     477             : 
     478             :   // Reg 1 is never read, so should be dead.
     479           1 :   builder.StoreAccumulatorInRegister(reg_1);
     480           1 :   expected_liveness.emplace_back("L.LL", "L.LL");
     481             : 
     482             :   builder.SuspendGenerator(
     483           1 :       reg_gen, interpreter::BytecodeUtils::NewRegisterList(0, 3), 0);
     484           1 :   expected_liveness.emplace_back("L.LL", "L.L.");
     485             : 
     486           1 :   builder.Bind(gen_jump_table, 0);
     487             : 
     488             :   builder.ResumeGenerator(reg_gen,
     489           1 :                           interpreter::BytecodeUtils::NewRegisterList(0, 1));
     490           1 :   expected_liveness.emplace_back("L.L.", "L...");
     491             : 
     492           1 :   builder.LoadAccumulatorWithRegister(reg_0);
     493           1 :   expected_liveness.emplace_back("L...", "...L");
     494             : 
     495           1 :   builder.Return();
     496           1 :   expected_liveness.emplace_back("...L", "....");
     497             : 
     498           1 :   Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
     499             : 
     500           1 :   EnsureLivenessMatches(bytecode, expected_liveness);
     501           1 : }
     502             : 
     503             : }  // namespace compiler
     504             : }  // namespace internal
     505        9264 : }  // namespace v8

Generated by: LCOV version 1.10