OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/base/utils/random-number-generator.h" | 5 #include "src/base/utils/random-number-generator.h" |
6 #include "src/compiler/register-allocator.h" | 6 #include "src/compiler/register-allocator.h" |
7 #include "test/unittests/test-utils.h" | 7 #include "test/unittests/test-utils.h" |
8 #include "testing/gmock/include/gmock/gmock.h" | 8 #include "testing/gmock/include/gmock/gmock.h" |
9 | 9 |
10 namespace v8 { | 10 namespace v8 { |
(...skipping 19 matching lines...) Expand all Loading... |
30 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); | 30 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); |
31 *loc++ = 0; | 31 *loc++ = 0; |
32 } | 32 } |
33 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { | 33 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { |
34 double_register_names_[i] = loc; | 34 double_register_names_[i] = loc; |
35 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; | 35 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; |
36 *loc++ = 0; | 36 *loc++ = 0; |
37 } | 37 } |
38 } | 38 } |
39 | 39 |
| 40 enum BlockCompletionType { kFallThrough, kBranch, kJump }; |
| 41 |
| 42 struct BlockCompletion { |
| 43 BlockCompletionType type_; |
| 44 int vreg_; |
| 45 int offset_0_; |
| 46 int offset_1_; |
| 47 }; |
| 48 |
| 49 static const int kInvalidJumpOffset = kMinInt; |
| 50 |
| 51 BlockCompletion FallThrough() { |
| 52 BlockCompletion completion = {kFallThrough, -1, 1, kInvalidJumpOffset}; |
| 53 return completion; |
| 54 } |
| 55 |
| 56 |
| 57 BlockCompletion Jump(int offset) { |
| 58 BlockCompletion completion = {kJump, -1, offset, kInvalidJumpOffset}; |
| 59 return completion; |
| 60 } |
| 61 |
| 62 |
| 63 BlockCompletion Branch(int vreg, int left_offset, int right_offset) { |
| 64 BlockCompletion completion = {kBranch, vreg, left_offset, right_offset}; |
| 65 return completion; |
| 66 } |
| 67 |
40 } // namespace | 68 } // namespace |
41 | 69 |
42 | 70 |
43 // TODO(dcarney): fake opcodes. | |
44 // TODO(dcarney): fix printing of sequence w.r.t fake opcodes and registers. | |
45 class RegisterAllocatorTest : public TestWithZone { | 71 class RegisterAllocatorTest : public TestWithZone { |
46 public: | 72 public: |
47 static const int kDefaultNRegs = 4; | 73 static const int kDefaultNRegs = 4; |
48 | 74 |
49 RegisterAllocatorTest() | 75 RegisterAllocatorTest() |
50 : num_general_registers_(kDefaultNRegs), | 76 : num_general_registers_(kDefaultNRegs), |
51 num_double_registers_(kDefaultNRegs), | 77 num_double_registers_(kDefaultNRegs), |
52 basic_blocks_(zone()), | |
53 instruction_blocks_(zone()), | 78 instruction_blocks_(zone()), |
54 current_block_(NULL) { | 79 current_block_(nullptr), |
| 80 is_last_block_(false) { |
55 InitializeRegisterNames(); | 81 InitializeRegisterNames(); |
56 } | 82 } |
57 | 83 |
| 84 void SetNumRegs(int num_general_registers, int num_double_registers) { |
| 85 CHECK(instruction_blocks_.empty()); |
| 86 num_general_registers_ = num_general_registers; |
| 87 num_double_registers_ = num_double_registers; |
| 88 } |
| 89 |
58 RegisterConfiguration* config() { | 90 RegisterConfiguration* config() { |
59 if (config_.is_empty()) { | 91 if (config_.is_empty()) { |
60 config_.Reset(new RegisterConfiguration( | 92 config_.Reset(new RegisterConfiguration( |
61 num_general_registers_, num_double_registers_, num_double_registers_, | 93 num_general_registers_, num_double_registers_, num_double_registers_, |
62 general_register_names_, double_register_names_)); | 94 general_register_names_, double_register_names_)); |
63 } | 95 } |
64 return config_.get(); | 96 return config_.get(); |
65 } | 97 } |
66 | 98 |
67 Frame* frame() { | 99 Frame* frame() { |
(...skipping 11 matching lines...) Expand all Loading... |
79 } | 111 } |
80 | 112 |
81 RegisterAllocator* allocator() { | 113 RegisterAllocator* allocator() { |
82 if (allocator_.is_empty()) { | 114 if (allocator_.is_empty()) { |
83 allocator_.Reset( | 115 allocator_.Reset( |
84 new RegisterAllocator(config(), zone(), frame(), sequence())); | 116 new RegisterAllocator(config(), zone(), frame(), sequence())); |
85 } | 117 } |
86 return allocator_.get(); | 118 return allocator_.get(); |
87 } | 119 } |
88 | 120 |
89 InstructionBlock* StartBlock(Rpo loop_header = Rpo::Invalid(), | 121 void StartLoop(int loop_blocks) { |
90 Rpo loop_end = Rpo::Invalid()) { | 122 CHECK(current_block_ == nullptr); |
91 CHECK(current_block_ == NULL); | 123 if (!loop_blocks_.empty()) { |
92 BasicBlock::Id block_id = | 124 CHECK(!loop_blocks_.back().loop_header_.IsValid()); |
93 BasicBlock::Id::FromSize(instruction_blocks_.size()); | |
94 BasicBlock* basic_block = new (zone()) BasicBlock(zone(), block_id); | |
95 basic_block->set_rpo_number(block_id.ToInt()); | |
96 basic_block->set_ao_number(block_id.ToInt()); | |
97 if (loop_header.IsValid()) { | |
98 basic_block->set_loop_depth(1); | |
99 basic_block->set_loop_header(basic_blocks_[loop_header.ToSize()]); | |
100 basic_block->set_loop_end(basic_blocks_[loop_end.ToSize()]); | |
101 } | 125 } |
102 InstructionBlock* instruction_block = | 126 LoopData loop_data = {Rpo::Invalid(), loop_blocks}; |
103 new (zone()) InstructionBlock(zone(), basic_block); | 127 loop_blocks_.push_back(loop_data); |
104 basic_blocks_.push_back(basic_block); | |
105 instruction_blocks_.push_back(instruction_block); | |
106 current_block_ = instruction_block; | |
107 sequence()->StartBlock(basic_block); | |
108 return instruction_block; | |
109 } | 128 } |
110 | 129 |
111 void EndBlock() { | 130 void EndLoop() { |
112 CHECK(current_block_ != NULL); | 131 CHECK(current_block_ == nullptr); |
113 sequence()->EndBlock(basic_blocks_[current_block_->rpo_number().ToSize()]); | 132 CHECK(!loop_blocks_.empty()); |
114 current_block_ = NULL; | 133 CHECK_EQ(0, loop_blocks_.back().expected_blocks_); |
| 134 loop_blocks_.pop_back(); |
| 135 } |
| 136 |
| 137 void StartLastBlock() { |
| 138 CHECK(!is_last_block_); |
| 139 is_last_block_ = true; |
| 140 NewBlock(); |
| 141 } |
| 142 |
| 143 void StartBlock() { |
| 144 CHECK(!is_last_block_); |
| 145 NewBlock(); |
| 146 } |
| 147 |
| 148 void EndBlock(BlockCompletion completion = FallThrough()) { |
| 149 completions_.push_back(completion); |
| 150 switch (completion.type_) { |
| 151 case kFallThrough: |
| 152 if (is_last_block_) break; |
| 153 // TODO(dcarney): we don't emit this after returns. |
| 154 EmitFallThrough(); |
| 155 break; |
| 156 case kJump: |
| 157 EmitJump(); |
| 158 break; |
| 159 case kBranch: |
| 160 EmitBranch(completion.vreg_); |
| 161 break; |
| 162 } |
| 163 CHECK(current_block_ != nullptr); |
| 164 sequence()->EndBlock(current_block_->rpo_number()); |
| 165 current_block_ = nullptr; |
115 } | 166 } |
116 | 167 |
117 void Allocate() { | 168 void Allocate() { |
118 if (FLAG_trace_alloc) { | 169 CHECK_EQ(nullptr, current_block_); |
| 170 CHECK(is_last_block_); |
| 171 WireBlocks(); |
| 172 if (FLAG_trace_alloc || FLAG_trace_turbo) { |
119 OFStream os(stdout); | 173 OFStream os(stdout); |
120 PrintableInstructionSequence printable = {config(), sequence()}; | 174 PrintableInstructionSequence printable = {config(), sequence()}; |
121 os << "Before: " << std::endl << printable << std::endl; | 175 os << "Before: " << std::endl << printable << std::endl; |
122 } | 176 } |
123 allocator()->Allocate(); | 177 allocator()->Allocate(); |
124 if (FLAG_trace_alloc) { | 178 if (FLAG_trace_alloc || FLAG_trace_turbo) { |
125 OFStream os(stdout); | 179 OFStream os(stdout); |
126 PrintableInstructionSequence printable = {config(), sequence()}; | 180 PrintableInstructionSequence printable = {config(), sequence()}; |
127 os << "After: " << std::endl << printable << std::endl; | 181 os << "After: " << std::endl << printable << std::endl; |
128 } | 182 } |
129 } | 183 } |
130 | 184 |
131 int NewReg() { return sequence()->NextVirtualRegister(); } | 185 int NewReg() { return sequence()->NextVirtualRegister(); } |
132 | 186 |
133 int Parameter() { | 187 int Parameter() { |
134 // TODO(dcarney): assert parameters before other instructions. | |
135 int vreg = NewReg(); | 188 int vreg = NewReg(); |
| 189 InstructionOperand* outputs[1]{UseRegister(vreg)}; |
| 190 Emit(kArchNop, 1, outputs); |
| 191 return vreg; |
| 192 } |
| 193 |
| 194 Instruction* Return(int vreg) { |
| 195 InstructionOperand* inputs[1]{UseRegister(vreg)}; |
| 196 return Emit(kArchRet, 0, nullptr, 1, inputs); |
| 197 } |
| 198 |
| 199 PhiInstruction* Phi(int vreg) { |
| 200 PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg()); |
| 201 phi->operands().push_back(vreg); |
| 202 current_block_->AddPhi(phi); |
| 203 return phi; |
| 204 } |
| 205 |
| 206 int DefineConstant(int32_t imm = 0) { |
| 207 int virtual_register = NewReg(); |
| 208 sequence()->AddConstant(virtual_register, Constant(imm)); |
136 InstructionOperand* outputs[1]{ | 209 InstructionOperand* outputs[1]{ |
137 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)}; | 210 ConstantOperand::Create(virtual_register, zone())}; |
138 sequence()->AddInstruction( | 211 Emit(kArchNop, 1, outputs); |
139 Instruction::New(zone(), kArchNop, 1, outputs, 0, NULL, 0, NULL)); | 212 return virtual_register; |
140 return vreg; | 213 } |
141 } | 214 |
142 | 215 ImmediateOperand* Immediate(int32_t imm = 0) { |
143 void Return(int vreg) { | 216 int index = sequence()->AddImmediate(Constant(imm)); |
144 InstructionOperand* inputs[1]{ | 217 return ImmediateOperand::Create(index, zone()); |
145 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)}; | 218 } |
146 sequence()->AddInstruction( | 219 |
147 Instruction::New(zone(), kArchNop, 0, NULL, 1, inputs, 0, NULL)); | 220 Instruction* EmitFRI(int output_vreg, int input_vreg_0) { |
148 } | 221 InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)}; |
149 | 222 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Immediate()}; |
150 Instruction* Emit(int output_vreg, int input_vreg_0, int input_vreg_1) { | 223 return Emit(kArchNop, 1, outputs, 2, inputs); |
151 InstructionOperand* outputs[1]{ | 224 } |
152 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, output_vreg)}; | 225 |
153 InstructionOperand* inputs[2]{ | 226 Instruction* EmitFRU(int output_vreg, int input_vreg_0, int input_vreg_1) { |
154 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, input_vreg_0), | 227 InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)}; |
155 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, input_vreg_1)}; | 228 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Use(input_vreg_1)}; |
156 Instruction* instruction = | 229 return Emit(kArchNop, 1, outputs, 2, inputs); |
157 Instruction::New(zone(), kArchNop, 1, outputs, 2, inputs, 0, NULL); | 230 } |
158 sequence()->AddInstruction(instruction); | 231 |
159 return instruction; | 232 Instruction* EmitRRR(int output_vreg, int input_vreg_0, int input_vreg_1) { |
| 233 InstructionOperand* outputs[1]{UseRegister(output_vreg)}; |
| 234 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), |
| 235 UseRegister(input_vreg_1)}; |
| 236 return Emit(kArchNop, 1, outputs, 2, inputs); |
160 } | 237 } |
161 | 238 |
162 private: | 239 private: |
163 InstructionOperand* Unallocated(UnallocatedOperand::ExtendedPolicy policy, | 240 InstructionOperand* Unallocated(int vreg, |
164 int vreg) { | 241 UnallocatedOperand::ExtendedPolicy policy) { |
165 UnallocatedOperand* op = | 242 UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy); |
166 new (zone()) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER); | |
167 op->set_virtual_register(vreg); | 243 op->set_virtual_register(vreg); |
168 return op; | 244 return op; |
169 } | 245 } |
170 | 246 |
171 int num_general_registers_; | 247 InstructionOperand* Unallocated(int vreg, |
172 int num_double_registers_; | 248 UnallocatedOperand::ExtendedPolicy policy, |
| 249 UnallocatedOperand::Lifetime lifetime) { |
| 250 UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy, lifetime); |
| 251 op->set_virtual_register(vreg); |
| 252 return op; |
| 253 } |
| 254 |
| 255 InstructionOperand* UseRegister(int vreg) { |
| 256 return Unallocated(vreg, UnallocatedOperand::MUST_HAVE_REGISTER); |
| 257 } |
| 258 |
| 259 InstructionOperand* DefineSameAsFirst(int vreg) { |
| 260 return Unallocated(vreg, UnallocatedOperand::SAME_AS_FIRST_INPUT); |
| 261 } |
| 262 |
| 263 InstructionOperand* Use(int vreg) { |
| 264 return Unallocated(vreg, UnallocatedOperand::NONE, |
| 265 UnallocatedOperand::USED_AT_START); |
| 266 } |
| 267 |
| 268 void EmitBranch(int vreg) { |
| 269 InstructionOperand* inputs[4]{UseRegister(vreg), Immediate(), Immediate(), |
| 270 Immediate()}; |
| 271 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) | |
| 272 FlagsConditionField::encode(kEqual); |
| 273 Instruction* instruction = |
| 274 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl(); |
| 275 sequence()->AddInstruction(instruction); |
| 276 } |
| 277 |
| 278 void EmitFallThrough() { |
| 279 Instruction* instruction = |
| 280 NewInstruction(kArchNop, 0, nullptr)->MarkAsControl(); |
| 281 sequence()->AddInstruction(instruction); |
| 282 } |
| 283 |
| 284 void EmitJump() { |
| 285 InstructionOperand* inputs[1]{Immediate()}; |
| 286 Instruction* instruction = |
| 287 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl(); |
| 288 sequence()->AddInstruction(instruction); |
| 289 } |
| 290 |
| 291 Instruction* NewInstruction(InstructionCode code, size_t outputs_size, |
| 292 InstructionOperand** outputs, |
| 293 size_t inputs_size = 0, |
| 294 InstructionOperand* *inputs = nullptr, |
| 295 size_t temps_size = 0, |
| 296 InstructionOperand* *temps = nullptr) { |
| 297 CHECK_NE(nullptr, current_block_); |
| 298 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size, |
| 299 inputs, temps_size, temps); |
| 300 } |
| 301 |
| 302 Instruction* Emit(InstructionCode code, size_t outputs_size, |
| 303 InstructionOperand** outputs, size_t inputs_size = 0, |
| 304 InstructionOperand* *inputs = nullptr, |
| 305 size_t temps_size = 0, |
| 306 InstructionOperand* *temps = nullptr) { |
| 307 Instruction* instruction = NewInstruction( |
| 308 code, outputs_size, outputs, inputs_size, inputs, temps_size, temps); |
| 309 sequence()->AddInstruction(instruction); |
| 310 return instruction; |
| 311 } |
| 312 |
| 313 InstructionBlock* NewBlock() { |
| 314 CHECK(current_block_ == nullptr); |
| 315 BasicBlock::Id block_id = |
| 316 BasicBlock::Id::FromSize(instruction_blocks_.size()); |
| 317 Rpo rpo = Rpo::FromInt(block_id.ToInt()); |
| 318 Rpo loop_header = Rpo::Invalid(); |
| 319 Rpo loop_end = Rpo::Invalid(); |
| 320 if (!loop_blocks_.empty()) { |
| 321 auto& loop_data = loop_blocks_.back(); |
| 322 // This is a loop header. |
| 323 if (!loop_data.loop_header_.IsValid()) { |
| 324 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_); |
| 325 loop_data.expected_blocks_--; |
| 326 loop_data.loop_header_ = rpo; |
| 327 } else { |
| 328 // This is a loop body. |
| 329 CHECK_NE(0, loop_data.expected_blocks_); |
| 330 // TODO(dcarney): handle nested loops. |
| 331 loop_data.expected_blocks_--; |
| 332 loop_header = loop_data.loop_header_; |
| 333 } |
| 334 } |
| 335 // Construct instruction block. |
| 336 InstructionBlock* instruction_block = new (zone()) InstructionBlock( |
| 337 zone(), block_id, rpo, rpo, loop_header, loop_end, false); |
| 338 instruction_blocks_.push_back(instruction_block); |
| 339 current_block_ = instruction_block; |
| 340 sequence()->StartBlock(rpo); |
| 341 return instruction_block; |
| 342 } |
| 343 |
| 344 void WireBlocks() { |
| 345 CHECK(instruction_blocks_.size() == completions_.size()); |
| 346 size_t offset = 0; |
| 347 size_t size = instruction_blocks_.size(); |
| 348 for (const auto& completion : completions_) { |
| 349 switch (completion.type_) { |
| 350 case kFallThrough: |
| 351 if (offset == size - 1) break; |
| 352 // Fallthrough. |
| 353 case kJump: |
| 354 WireBlock(offset, completion.offset_0_); |
| 355 break; |
| 356 case kBranch: |
| 357 WireBlock(offset, completion.offset_0_); |
| 358 WireBlock(offset, completion.offset_1_); |
| 359 break; |
| 360 } |
| 361 ++offset; |
| 362 } |
| 363 } |
| 364 |
| 365 void WireBlock(size_t block_offset, int jump_offset) { |
| 366 size_t target_block_offset = |
| 367 block_offset + static_cast<size_t>(jump_offset); |
| 368 CHECK(block_offset < instruction_blocks_.size()); |
| 369 CHECK(target_block_offset < instruction_blocks_.size()); |
| 370 InstructionBlock* block = instruction_blocks_[block_offset]; |
| 371 InstructionBlock* target = instruction_blocks_[target_block_offset]; |
| 372 block->successors().push_back(target->rpo_number()); |
| 373 target->predecessors().push_back(block->rpo_number()); |
| 374 } |
| 375 |
| 376 struct LoopData { |
| 377 Rpo loop_header_; |
| 378 int expected_blocks_; |
| 379 }; |
| 380 typedef std::vector<LoopData> LoopBlocks; |
| 381 typedef std::vector<BlockCompletion> Completions; |
| 382 |
173 SmartPointer<RegisterConfiguration> config_; | 383 SmartPointer<RegisterConfiguration> config_; |
174 ZoneVector<BasicBlock*> basic_blocks_; | |
175 InstructionBlocks instruction_blocks_; | |
176 InstructionBlock* current_block_; | |
177 SmartPointer<Frame> frame_; | 384 SmartPointer<Frame> frame_; |
178 SmartPointer<RegisterAllocator> allocator_; | 385 SmartPointer<RegisterAllocator> allocator_; |
179 SmartPointer<InstructionSequence> sequence_; | 386 SmartPointer<InstructionSequence> sequence_; |
| 387 int num_general_registers_; |
| 388 int num_double_registers_; |
| 389 |
| 390 // Block building state. |
| 391 InstructionBlocks instruction_blocks_; |
| 392 Completions completions_; |
| 393 LoopBlocks loop_blocks_; |
| 394 InstructionBlock* current_block_; |
| 395 bool is_last_block_; |
180 }; | 396 }; |
181 | 397 |
182 | 398 |
183 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { | 399 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { |
184 StartBlock(); | 400 StartLastBlock(); |
185 int a_reg = Parameter(); | 401 int a_reg = Parameter(); |
186 int b_reg = Parameter(); | 402 int b_reg = Parameter(); |
187 int c_reg = NewReg(); | 403 int c_reg = NewReg(); |
188 Instruction* res = Emit(c_reg, a_reg, b_reg); | 404 Instruction* res = EmitRRR(c_reg, a_reg, b_reg); |
189 Return(c_reg); | 405 Return(c_reg); |
190 EndBlock(); | 406 EndBlock(); |
191 | 407 |
192 Allocate(); | 408 Allocate(); |
193 | 409 |
194 ASSERT_TRUE(res->OutputAt(0)->IsRegister()); | 410 ASSERT_TRUE(res->OutputAt(0)->IsRegister()); |
195 } | 411 } |
196 | 412 |
| 413 |
| 414 TEST_F(RegisterAllocatorTest, SimpleLoop) { |
| 415 // i = K; |
| 416 // while(true) { i++ } |
| 417 |
| 418 StartBlock(); |
| 419 int i_reg = DefineConstant(); |
| 420 EndBlock(); |
| 421 |
| 422 { |
| 423 StartLoop(1); |
| 424 |
| 425 StartLastBlock(); |
| 426 PhiInstruction* phi = Phi(i_reg); |
| 427 int ipp = NewReg(); |
| 428 EmitFRU(ipp, phi->virtual_register(), DefineConstant()); |
| 429 phi->operands().push_back(ipp); |
| 430 EndBlock(Jump(0)); |
| 431 |
| 432 EndLoop(); |
| 433 } |
| 434 |
| 435 Allocate(); |
| 436 } |
| 437 |
| 438 |
| 439 TEST_F(RegisterAllocatorTest, SimpleBranch) { |
| 440 // return i ? K1 : K2 |
| 441 StartBlock(); |
| 442 int i_reg = DefineConstant(); |
| 443 EndBlock(Branch(i_reg, 1, 2)); |
| 444 |
| 445 StartBlock(); |
| 446 Return(DefineConstant()); |
| 447 EndBlock(); |
| 448 |
| 449 StartLastBlock(); |
| 450 Return(DefineConstant()); |
| 451 EndBlock(); |
| 452 |
| 453 Allocate(); |
| 454 } |
| 455 |
197 } // namespace compiler | 456 } // namespace compiler |
198 } // namespace internal | 457 } // namespace internal |
199 } // namespace v8 | 458 } // namespace v8 |
OLD | NEW |