OLD | NEW |
(Empty) | |
| 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 "src/base/utils/random-number-generator.h" |
| 6 #include "src/compiler/pipeline.h" |
| 7 #include "test/unittests/compiler/instruction-sequence-unittest.h" |
| 8 #include "test/unittests/test-utils.h" |
| 9 #include "testing/gmock/include/gmock/gmock.h" |
| 10 |
| 11 namespace v8 { |
| 12 namespace internal { |
| 13 namespace compiler { |
| 14 |
| 15 static const char* |
| 16 general_register_names_[RegisterConfiguration::kMaxGeneralRegisters]; |
| 17 static const char* |
| 18 double_register_names_[RegisterConfiguration::kMaxDoubleRegisters]; |
| 19 static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters + |
| 20 RegisterConfiguration::kMaxDoubleRegisters)]; |
| 21 |
| 22 |
| 23 static void InitializeRegisterNames() { |
| 24 char* loc = register_names_; |
| 25 for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) { |
| 26 general_register_names_[i] = loc; |
| 27 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); |
| 28 *loc++ = 0; |
| 29 } |
| 30 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { |
| 31 double_register_names_[i] = loc; |
| 32 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; |
| 33 *loc++ = 0; |
| 34 } |
| 35 } |
| 36 |
| 37 |
| 38 InstructionSequenceTest::InstructionSequenceTest() |
| 39 : sequence_(nullptr), |
| 40 num_general_registers_(kDefaultNRegs), |
| 41 num_double_registers_(kDefaultNRegs), |
| 42 instruction_blocks_(zone()), |
| 43 current_instruction_index_(-1), |
| 44 current_block_(nullptr), |
| 45 block_returns_(false) { |
| 46 InitializeRegisterNames(); |
| 47 } |
| 48 |
| 49 |
| 50 void InstructionSequenceTest::SetNumRegs(int num_general_registers, |
| 51 int num_double_registers) { |
| 52 CHECK(config_.is_empty()); |
| 53 CHECK(instructions_.empty()); |
| 54 CHECK(instruction_blocks_.empty()); |
| 55 num_general_registers_ = num_general_registers; |
| 56 num_double_registers_ = num_double_registers; |
| 57 } |
| 58 |
| 59 |
| 60 RegisterConfiguration* InstructionSequenceTest::config() { |
| 61 if (config_.is_empty()) { |
| 62 config_.Reset(new RegisterConfiguration( |
| 63 num_general_registers_, num_double_registers_, num_double_registers_, |
| 64 general_register_names_, double_register_names_)); |
| 65 } |
| 66 return config_.get(); |
| 67 } |
| 68 |
| 69 |
| 70 InstructionSequence* InstructionSequenceTest::sequence() { |
| 71 if (sequence_ == nullptr) { |
| 72 sequence_ = new (zone()) InstructionSequence(zone(), &instruction_blocks_); |
| 73 } |
| 74 return sequence_; |
| 75 } |
| 76 |
| 77 |
| 78 void InstructionSequenceTest::StartLoop(int loop_blocks) { |
| 79 CHECK(current_block_ == nullptr); |
| 80 if (!loop_blocks_.empty()) { |
| 81 CHECK(!loop_blocks_.back().loop_header_.IsValid()); |
| 82 } |
| 83 LoopData loop_data = {Rpo::Invalid(), loop_blocks}; |
| 84 loop_blocks_.push_back(loop_data); |
| 85 } |
| 86 |
| 87 |
| 88 void InstructionSequenceTest::EndLoop() { |
| 89 CHECK(current_block_ == nullptr); |
| 90 CHECK(!loop_blocks_.empty()); |
| 91 CHECK_EQ(0, loop_blocks_.back().expected_blocks_); |
| 92 loop_blocks_.pop_back(); |
| 93 } |
| 94 |
| 95 |
| 96 void InstructionSequenceTest::StartBlock() { |
| 97 block_returns_ = false; |
| 98 NewBlock(); |
| 99 } |
| 100 |
| 101 |
| 102 int InstructionSequenceTest::EndBlock(BlockCompletion completion) { |
| 103 int instruction_index = kMinInt; |
| 104 if (block_returns_) { |
| 105 CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough); |
| 106 completion.type_ = kBlockEnd; |
| 107 } |
| 108 switch (completion.type_) { |
| 109 case kBlockEnd: |
| 110 break; |
| 111 case kFallThrough: |
| 112 instruction_index = EmitFallThrough(); |
| 113 break; |
| 114 case kJump: |
| 115 CHECK(!block_returns_); |
| 116 instruction_index = EmitJump(); |
| 117 break; |
| 118 case kBranch: |
| 119 CHECK(!block_returns_); |
| 120 instruction_index = EmitBranch(completion.op_); |
| 121 break; |
| 122 } |
| 123 completions_.push_back(completion); |
| 124 CHECK(current_block_ != nullptr); |
| 125 sequence()->EndBlock(current_block_->rpo_number()); |
| 126 current_block_ = nullptr; |
| 127 return instruction_index; |
| 128 } |
| 129 |
| 130 |
| 131 InstructionSequenceTest::TestOperand InstructionSequenceTest::Imm(int32_t imm) { |
| 132 int index = sequence()->AddImmediate(Constant(imm)); |
| 133 return TestOperand(kImmediate, index); |
| 134 } |
| 135 |
| 136 |
| 137 InstructionSequenceTest::VReg InstructionSequenceTest::Define( |
| 138 TestOperand output_op) { |
| 139 VReg vreg = NewReg(); |
| 140 InstructionOperand* outputs[1]{ConvertOutputOp(vreg, output_op)}; |
| 141 Emit(vreg.value_, kArchNop, 1, outputs); |
| 142 return vreg; |
| 143 } |
| 144 |
| 145 |
| 146 int InstructionSequenceTest::Return(TestOperand input_op_0) { |
| 147 block_returns_ = true; |
| 148 InstructionOperand* inputs[1]{ConvertInputOp(input_op_0)}; |
| 149 return Emit(NewIndex(), kArchRet, 0, nullptr, 1, inputs); |
| 150 } |
| 151 |
| 152 |
| 153 PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0, |
| 154 VReg incoming_vreg_1, |
| 155 VReg incoming_vreg_2, |
| 156 VReg incoming_vreg_3) { |
| 157 auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, 10); |
| 158 VReg inputs[] = {incoming_vreg_0, incoming_vreg_1, incoming_vreg_2, |
| 159 incoming_vreg_3}; |
| 160 for (size_t i = 0; i < arraysize(inputs); ++i) { |
| 161 if (inputs[i].value_ == kNoValue) break; |
| 162 Extend(phi, inputs[i]); |
| 163 } |
| 164 current_block_->AddPhi(phi); |
| 165 return phi; |
| 166 } |
| 167 |
| 168 |
| 169 void InstructionSequenceTest::Extend(PhiInstruction* phi, VReg vreg) { |
| 170 phi->Extend(zone(), vreg.value_); |
| 171 } |
| 172 |
| 173 |
| 174 InstructionSequenceTest::VReg InstructionSequenceTest::DefineConstant( |
| 175 int32_t imm) { |
| 176 VReg vreg = NewReg(); |
| 177 sequence()->AddConstant(vreg.value_, Constant(imm)); |
| 178 InstructionOperand* outputs[1]{ConstantOperand::Create(vreg.value_, zone())}; |
| 179 Emit(vreg.value_, kArchNop, 1, outputs); |
| 180 return vreg; |
| 181 } |
| 182 |
| 183 |
| 184 int InstructionSequenceTest::EmitNop() { return Emit(NewIndex(), kArchNop); } |
| 185 |
| 186 |
| 187 int InstructionSequenceTest::EmitI(TestOperand input_op_0) { |
| 188 InstructionOperand* inputs[1]{ConvertInputOp(input_op_0)}; |
| 189 return Emit(NewIndex(), kArchNop, 0, nullptr, 1, inputs); |
| 190 } |
| 191 |
| 192 |
| 193 InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI( |
| 194 TestOperand output_op, TestOperand input_op_0) { |
| 195 VReg output_vreg = NewReg(); |
| 196 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)}; |
| 197 InstructionOperand* inputs[1]{ConvertInputOp(input_op_0)}; |
| 198 Emit(output_vreg.value_, kArchNop, 1, outputs, 1, inputs); |
| 199 return output_vreg; |
| 200 } |
| 201 |
| 202 |
| 203 InstructionSequenceTest::VReg InstructionSequenceTest::EmitOII( |
| 204 TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1) { |
| 205 VReg output_vreg = NewReg(); |
| 206 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)}; |
| 207 InstructionOperand* inputs[2]{ConvertInputOp(input_op_0), |
| 208 ConvertInputOp(input_op_1)}; |
| 209 Emit(output_vreg.value_, kArchNop, 1, outputs, 2, inputs); |
| 210 return output_vreg; |
| 211 } |
| 212 |
| 213 |
| 214 InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall( |
| 215 TestOperand output_op, size_t input_size, TestOperand* inputs) { |
| 216 VReg output_vreg = NewReg(); |
| 217 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)}; |
| 218 CHECK(UnallocatedOperand::cast(outputs[0])->HasFixedPolicy()); |
| 219 InstructionOperand** mapped_inputs = |
| 220 zone()->NewArray<InstructionOperand*>(static_cast<int>(input_size)); |
| 221 for (size_t i = 0; i < input_size; ++i) { |
| 222 mapped_inputs[i] = ConvertInputOp(inputs[i]); |
| 223 } |
| 224 Emit(output_vreg.value_, kArchCallCodeObject, 1, outputs, input_size, |
| 225 mapped_inputs, 0, nullptr, true); |
| 226 return output_vreg; |
| 227 } |
| 228 |
| 229 |
| 230 InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall( |
| 231 TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1, |
| 232 TestOperand input_op_2, TestOperand input_op_3) { |
| 233 TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3}; |
| 234 size_t size = 0; |
| 235 for (; size < arraysize(inputs); ++size) { |
| 236 if (inputs[size].type_ == kInvalid) break; |
| 237 } |
| 238 return EmitCall(output_op, size, inputs); |
| 239 } |
| 240 |
| 241 |
| 242 const Instruction* InstructionSequenceTest::GetInstruction( |
| 243 int instruction_index) { |
| 244 auto it = instructions_.find(instruction_index); |
| 245 CHECK(it != instructions_.end()); |
| 246 return it->second; |
| 247 } |
| 248 |
| 249 |
| 250 int InstructionSequenceTest::EmitBranch(TestOperand input_op) { |
| 251 InstructionOperand* inputs[4]{ConvertInputOp(input_op), ConvertInputOp(Imm()), |
| 252 ConvertInputOp(Imm()), ConvertInputOp(Imm())}; |
| 253 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) | |
| 254 FlagsConditionField::encode(kEqual); |
| 255 auto instruction = |
| 256 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl(); |
| 257 return AddInstruction(NewIndex(), instruction); |
| 258 } |
| 259 |
| 260 |
| 261 int InstructionSequenceTest::EmitFallThrough() { |
| 262 auto instruction = NewInstruction(kArchNop, 0, nullptr)->MarkAsControl(); |
| 263 return AddInstruction(NewIndex(), instruction); |
| 264 } |
| 265 |
| 266 |
| 267 int InstructionSequenceTest::EmitJump() { |
| 268 InstructionOperand* inputs[1]{ConvertInputOp(Imm())}; |
| 269 auto instruction = |
| 270 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl(); |
| 271 return AddInstruction(NewIndex(), instruction); |
| 272 } |
| 273 |
| 274 |
| 275 Instruction* InstructionSequenceTest::NewInstruction( |
| 276 InstructionCode code, size_t outputs_size, InstructionOperand** outputs, |
| 277 size_t inputs_size, InstructionOperand** inputs, size_t temps_size, |
| 278 InstructionOperand** temps) { |
| 279 CHECK_NE(nullptr, current_block_); |
| 280 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size, |
| 281 inputs, temps_size, temps); |
| 282 } |
| 283 |
| 284 |
| 285 InstructionOperand* InstructionSequenceTest::Unallocated( |
| 286 TestOperand op, UnallocatedOperand::ExtendedPolicy policy) { |
| 287 auto unallocated = new (zone()) UnallocatedOperand(policy); |
| 288 unallocated->set_virtual_register(op.vreg_.value_); |
| 289 return unallocated; |
| 290 } |
| 291 |
| 292 |
| 293 InstructionOperand* InstructionSequenceTest::Unallocated( |
| 294 TestOperand op, UnallocatedOperand::ExtendedPolicy policy, |
| 295 UnallocatedOperand::Lifetime lifetime) { |
| 296 auto unallocated = new (zone()) UnallocatedOperand(policy, lifetime); |
| 297 unallocated->set_virtual_register(op.vreg_.value_); |
| 298 return unallocated; |
| 299 } |
| 300 |
| 301 |
| 302 InstructionOperand* InstructionSequenceTest::Unallocated( |
| 303 TestOperand op, UnallocatedOperand::ExtendedPolicy policy, int index) { |
| 304 auto unallocated = new (zone()) UnallocatedOperand(policy, index); |
| 305 unallocated->set_virtual_register(op.vreg_.value_); |
| 306 return unallocated; |
| 307 } |
| 308 |
| 309 |
| 310 InstructionOperand* InstructionSequenceTest::Unallocated( |
| 311 TestOperand op, UnallocatedOperand::BasicPolicy policy, int index) { |
| 312 auto unallocated = new (zone()) UnallocatedOperand(policy, index); |
| 313 unallocated->set_virtual_register(op.vreg_.value_); |
| 314 return unallocated; |
| 315 } |
| 316 |
| 317 |
| 318 InstructionOperand* InstructionSequenceTest::ConvertInputOp(TestOperand op) { |
| 319 if (op.type_ == kImmediate) { |
| 320 CHECK_EQ(op.vreg_.value_, kNoValue); |
| 321 return ImmediateOperand::Create(op.value_, zone()); |
| 322 } |
| 323 CHECK_NE(op.vreg_.value_, kNoValue); |
| 324 switch (op.type_) { |
| 325 case kNone: |
| 326 return Unallocated(op, UnallocatedOperand::NONE, |
| 327 UnallocatedOperand::USED_AT_START); |
| 328 case kRegister: |
| 329 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER, |
| 330 UnallocatedOperand::USED_AT_START); |
| 331 case kFixedRegister: |
| 332 CHECK(0 <= op.value_ && op.value_ < num_general_registers_); |
| 333 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_); |
| 334 case kFixedSlot: |
| 335 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_); |
| 336 default: |
| 337 break; |
| 338 } |
| 339 CHECK(false); |
| 340 return NULL; |
| 341 } |
| 342 |
| 343 |
| 344 InstructionOperand* InstructionSequenceTest::ConvertOutputOp(VReg vreg, |
| 345 TestOperand op) { |
| 346 CHECK_EQ(op.vreg_.value_, kNoValue); |
| 347 op.vreg_ = vreg; |
| 348 switch (op.type_) { |
| 349 case kSameAsFirst: |
| 350 return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT); |
| 351 case kRegister: |
| 352 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER); |
| 353 case kFixedSlot: |
| 354 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_); |
| 355 case kFixedRegister: |
| 356 CHECK(0 <= op.value_ && op.value_ < num_general_registers_); |
| 357 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_); |
| 358 default: |
| 359 break; |
| 360 } |
| 361 CHECK(false); |
| 362 return NULL; |
| 363 } |
| 364 |
| 365 |
| 366 InstructionBlock* InstructionSequenceTest::NewBlock() { |
| 367 CHECK(current_block_ == nullptr); |
| 368 auto block_id = BasicBlock::Id::FromSize(instruction_blocks_.size()); |
| 369 Rpo rpo = Rpo::FromInt(block_id.ToInt()); |
| 370 Rpo loop_header = Rpo::Invalid(); |
| 371 Rpo loop_end = Rpo::Invalid(); |
| 372 if (!loop_blocks_.empty()) { |
| 373 auto& loop_data = loop_blocks_.back(); |
| 374 // This is a loop header. |
| 375 if (!loop_data.loop_header_.IsValid()) { |
| 376 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_); |
| 377 loop_data.expected_blocks_--; |
| 378 loop_data.loop_header_ = rpo; |
| 379 } else { |
| 380 // This is a loop body. |
| 381 CHECK_NE(0, loop_data.expected_blocks_); |
| 382 // TODO(dcarney): handle nested loops. |
| 383 loop_data.expected_blocks_--; |
| 384 loop_header = loop_data.loop_header_; |
| 385 } |
| 386 } |
| 387 // Construct instruction block. |
| 388 auto instruction_block = new (zone()) InstructionBlock( |
| 389 zone(), block_id, rpo, rpo, loop_header, loop_end, false); |
| 390 instruction_blocks_.push_back(instruction_block); |
| 391 current_block_ = instruction_block; |
| 392 sequence()->StartBlock(rpo); |
| 393 return instruction_block; |
| 394 } |
| 395 |
| 396 |
| 397 void InstructionSequenceTest::WireBlocks() { |
| 398 CHECK_EQ(nullptr, current_block()); |
| 399 CHECK(instruction_blocks_.size() == completions_.size()); |
| 400 size_t offset = 0; |
| 401 for (const auto& completion : completions_) { |
| 402 switch (completion.type_) { |
| 403 case kBlockEnd: |
| 404 break; |
| 405 case kFallThrough: // Fallthrough. |
| 406 case kJump: |
| 407 WireBlock(offset, completion.offset_0_); |
| 408 break; |
| 409 case kBranch: |
| 410 WireBlock(offset, completion.offset_0_); |
| 411 WireBlock(offset, completion.offset_1_); |
| 412 break; |
| 413 } |
| 414 ++offset; |
| 415 } |
| 416 } |
| 417 |
| 418 |
| 419 void InstructionSequenceTest::WireBlock(size_t block_offset, int jump_offset) { |
| 420 size_t target_block_offset = block_offset + static_cast<size_t>(jump_offset); |
| 421 CHECK(block_offset < instruction_blocks_.size()); |
| 422 CHECK(target_block_offset < instruction_blocks_.size()); |
| 423 auto block = instruction_blocks_[block_offset]; |
| 424 auto target = instruction_blocks_[target_block_offset]; |
| 425 block->successors().push_back(target->rpo_number()); |
| 426 target->predecessors().push_back(block->rpo_number()); |
| 427 } |
| 428 |
| 429 |
| 430 int InstructionSequenceTest::Emit(int instruction_index, InstructionCode code, |
| 431 size_t outputs_size, |
| 432 InstructionOperand** outputs, |
| 433 size_t inputs_size, |
| 434 InstructionOperand** inputs, |
| 435 size_t temps_size, InstructionOperand** temps, |
| 436 bool is_call) { |
| 437 auto instruction = NewInstruction(code, outputs_size, outputs, inputs_size, |
| 438 inputs, temps_size, temps); |
| 439 if (is_call) instruction->MarkAsCall(); |
| 440 return AddInstruction(instruction_index, instruction); |
| 441 } |
| 442 |
| 443 |
| 444 int InstructionSequenceTest::AddInstruction(int instruction_index, |
| 445 Instruction* instruction) { |
| 446 sequence()->AddInstruction(instruction); |
| 447 return instruction_index; |
| 448 } |
| 449 |
| 450 } // namespace compiler |
| 451 } // namespace internal |
| 452 } // namespace v8 |
OLD | NEW |