Home | History | Annotate | Download | only in interpreter
      1 // Copyright 2016 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/factory.h"
      8 #include "src/interpreter/bytecode-label.h"
      9 #include "src/interpreter/bytecode-register-optimizer.h"
     10 #include "src/objects-inl.h"
     11 #include "src/objects.h"
     12 #include "test/unittests/test-utils.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 namespace interpreter {
     17 
     18 class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
     19                                       public TestWithIsolateAndZone {
     20  public:
     21   BytecodeRegisterOptimizerTest() {}
     22   ~BytecodeRegisterOptimizerTest() override { delete register_allocator_; }
     23 
     24   void Initialize(int number_of_parameters, int number_of_locals) {
     25     register_allocator_ =
     26         new TemporaryRegisterAllocator(zone(), number_of_locals);
     27     register_optimizer_ = new (zone()) BytecodeRegisterOptimizer(
     28         zone(), register_allocator_, number_of_parameters, this);
     29   }
     30 
     31   void Write(BytecodeNode* node) override { output_.push_back(*node); }
     32   void WriteJump(BytecodeNode* node, BytecodeLabel* label) override {
     33     output_.push_back(*node);
     34   }
     35   void BindLabel(BytecodeLabel* label) override {}
     36   void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {}
     37   Handle<BytecodeArray> ToBytecodeArray(
     38       int fixed_register_count, int parameter_count,
     39       Handle<FixedArray> handle_table) override {
     40     return Handle<BytecodeArray>();
     41   }
     42 
     43   TemporaryRegisterAllocator* allocator() { return register_allocator_; }
     44   BytecodeRegisterOptimizer* optimizer() { return register_optimizer_; }
     45 
     46   Register NewTemporary() {
     47     return Register(allocator()->BorrowTemporaryRegister());
     48   }
     49 
     50   void KillTemporary(Register reg) {
     51     allocator()->ReturnTemporaryRegister(reg.index());
     52   }
     53 
     54   size_t write_count() const { return output_.size(); }
     55   const BytecodeNode& last_written() const { return output_.back(); }
     56   const std::vector<BytecodeNode>* output() { return &output_; }
     57 
     58  private:
     59   TemporaryRegisterAllocator* register_allocator_;
     60   BytecodeRegisterOptimizer* register_optimizer_;
     61 
     62   std::vector<BytecodeNode> output_;
     63 };
     64 
     65 // Sanity tests.
     66 
     67 TEST_F(BytecodeRegisterOptimizerTest, WriteNop) {
     68   Initialize(1, 1);
     69   BytecodeNode node(Bytecode::kNop);
     70   optimizer()->Write(&node);
     71   CHECK_EQ(write_count(), 1);
     72   CHECK_EQ(node, last_written());
     73 }
     74 
     75 TEST_F(BytecodeRegisterOptimizerTest, WriteNopExpression) {
     76   Initialize(1, 1);
     77   BytecodeNode node(Bytecode::kNop);
     78   node.source_info().MakeExpressionPosition(3);
     79   optimizer()->Write(&node);
     80   CHECK_EQ(write_count(), 1);
     81   CHECK_EQ(node, last_written());
     82 }
     83 
     84 TEST_F(BytecodeRegisterOptimizerTest, WriteNopStatement) {
     85   Initialize(1, 1);
     86   BytecodeNode node(Bytecode::kNop);
     87   node.source_info().MakeStatementPosition(3);
     88   optimizer()->Write(&node);
     89   CHECK_EQ(write_count(), 1);
     90   CHECK_EQ(node, last_written());
     91 }
     92 
     93 TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForJump) {
     94   Initialize(1, 1);
     95   Register temp = NewTemporary();
     96   BytecodeNode node(Bytecode::kStar, temp.ToOperand());
     97   optimizer()->Write(&node);
     98   CHECK_EQ(write_count(), 0);
     99   BytecodeLabel label;
    100   BytecodeNode jump(Bytecode::kJump, 0);
    101   optimizer()->WriteJump(&jump, &label);
    102   CHECK_EQ(write_count(), 2);
    103   CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
    104   CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
    105   CHECK_EQ(output()->at(1).bytecode(), Bytecode::kJump);
    106 }
    107 
    108 TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForBind) {
    109   Initialize(1, 1);
    110   Register temp = NewTemporary();
    111   BytecodeNode node(Bytecode::kStar, temp.ToOperand());
    112   optimizer()->Write(&node);
    113   CHECK_EQ(write_count(), 0);
    114   BytecodeLabel label;
    115   optimizer()->BindLabel(&label);
    116   CHECK_EQ(write_count(), 1);
    117   CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
    118   CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
    119 }
    120 
    121 // Basic Register Optimizations
    122 
    123 TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) {
    124   Initialize(3, 1);
    125   Register parameter = Register::FromParameterIndex(1, 3);
    126   BytecodeNode node0(Bytecode::kLdar, parameter.ToOperand());
    127   optimizer()->Write(&node0);
    128   CHECK_EQ(write_count(), 0);
    129   Register temp = NewTemporary();
    130   BytecodeNode node1(Bytecode::kStar, NewTemporary().ToOperand());
    131   optimizer()->Write(&node1);
    132   CHECK_EQ(write_count(), 0);
    133   KillTemporary(temp);
    134   CHECK_EQ(write_count(), 0);
    135   BytecodeNode node2(Bytecode::kReturn);
    136   optimizer()->Write(&node2);
    137   CHECK_EQ(write_count(), 2);
    138   CHECK_EQ(output()->at(0).bytecode(), Bytecode::kLdar);
    139   CHECK_EQ(output()->at(0).operand(0), parameter.ToOperand());
    140   CHECK_EQ(output()->at(1).bytecode(), Bytecode::kReturn);
    141 }
    142 
    143 TEST_F(BytecodeRegisterOptimizerTest, StoresToLocalsImmediate) {
    144   Initialize(3, 1);
    145   Register parameter = Register::FromParameterIndex(1, 3);
    146   BytecodeNode node0(Bytecode::kLdar, parameter.ToOperand());
    147   optimizer()->Write(&node0);
    148   CHECK_EQ(write_count(), 0);
    149   Register local = Register(0);
    150   BytecodeNode node1(Bytecode::kStar, local.ToOperand());
    151   optimizer()->Write(&node1);
    152   CHECK_EQ(write_count(), 1);
    153   CHECK_EQ(output()->at(0).bytecode(), Bytecode::kMov);
    154   CHECK_EQ(output()->at(0).operand(0), parameter.ToOperand());
    155   CHECK_EQ(output()->at(0).operand(1), local.ToOperand());
    156 
    157   BytecodeNode node2(Bytecode::kReturn);
    158   optimizer()->Write(&node2);
    159   CHECK_EQ(write_count(), 3);
    160   CHECK_EQ(output()->at(1).bytecode(), Bytecode::kLdar);
    161   CHECK_EQ(output()->at(1).operand(0), local.ToOperand());
    162   CHECK_EQ(output()->at(2).bytecode(), Bytecode::kReturn);
    163 }
    164 
    165 TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotMaterializedForInput) {
    166   Initialize(3, 1);
    167   Register parameter = Register::FromParameterIndex(1, 3);
    168   Register temp0 = NewTemporary();
    169   Register temp1 = NewTemporary();
    170   BytecodeNode node0(Bytecode::kMov, parameter.ToOperand(), temp0.ToOperand());
    171   optimizer()->Write(&node0);
    172   BytecodeNode node1(Bytecode::kMov, parameter.ToOperand(), temp1.ToOperand());
    173   optimizer()->Write(&node1);
    174   CHECK_EQ(write_count(), 0);
    175   BytecodeNode node2(Bytecode::kCallJSRuntime, 0, temp0.ToOperand(), 1);
    176   optimizer()->Write(&node2);
    177   CHECK_EQ(write_count(), 1);
    178   CHECK_EQ(output()->at(0).bytecode(), Bytecode::kCallJSRuntime);
    179   CHECK_EQ(output()->at(0).operand(0), 0);
    180   CHECK_EQ(output()->at(0).operand(1), parameter.ToOperand());
    181   CHECK_EQ(output()->at(0).operand(2), 1);
    182 }
    183 
    184 TEST_F(BytecodeRegisterOptimizerTest, RangeOfTemporariesMaterializedForInput) {
    185   Initialize(3, 1);
    186   Register parameter = Register::FromParameterIndex(1, 3);
    187   Register temp0 = NewTemporary();
    188   Register temp1 = NewTemporary();
    189   BytecodeNode node0(Bytecode::kLdaSmi, 3);
    190   optimizer()->Write(&node0);
    191   CHECK_EQ(write_count(), 1);
    192   BytecodeNode node1(Bytecode::kStar, temp0.ToOperand());
    193   optimizer()->Write(&node1);
    194   BytecodeNode node2(Bytecode::kMov, parameter.ToOperand(), temp1.ToOperand());
    195   optimizer()->Write(&node2);
    196   CHECK_EQ(write_count(), 1);
    197   BytecodeNode node3(Bytecode::kCallJSRuntime, 0, temp0.ToOperand(), 2);
    198   optimizer()->Write(&node3);
    199   CHECK_EQ(write_count(), 4);
    200 
    201   CHECK_EQ(output()->at(0).bytecode(), Bytecode::kLdaSmi);
    202   CHECK_EQ(output()->at(0).operand(0), 3);
    203 
    204   CHECK_EQ(output()->at(1).bytecode(), Bytecode::kStar);
    205   CHECK_EQ(output()->at(1).operand(0), temp0.ToOperand());
    206 
    207   CHECK_EQ(output()->at(2).bytecode(), Bytecode::kMov);
    208   CHECK_EQ(output()->at(2).operand(0), parameter.ToOperand());
    209   CHECK_EQ(output()->at(2).operand(1), temp1.ToOperand());
    210 
    211   CHECK_EQ(output()->at(3).bytecode(), Bytecode::kCallJSRuntime);
    212   CHECK_EQ(output()->at(3).operand(0), 0);
    213   CHECK_EQ(output()->at(3).operand(1), temp0.ToOperand());
    214   CHECK_EQ(output()->at(3).operand(2), 2);
    215 }
    216 
    217 }  // namespace interpreter
    218 }  // namespace internal
    219 }  // namespace v8
    220