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