| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. |
| 6 #if defined(TARGET_ARCH_IA32) | 6 #if defined(TARGET_ARCH_IA32) |
| 7 | 7 |
| 8 #include "vm/flow_graph_compiler.h" | 8 #include "vm/flow_graph_compiler.h" |
| 9 | 9 |
| 10 #include "vm/ast_printer.h" |
| 10 #include "vm/compiler_stats.h" | 11 #include "vm/compiler_stats.h" |
| 12 #include "vm/debugger.h" |
| 13 #include "vm/il_printer.h" |
| 14 #include "vm/intrinsifier.h" |
| 15 #include "vm/locations.h" |
| 11 #include "vm/longjump.h" | 16 #include "vm/longjump.h" |
| 17 #include "vm/stub_code.h" |
| 12 | 18 |
| 13 namespace dart { | 19 namespace dart { |
| 14 | 20 |
| 21 DECLARE_FLAG(bool, code_comments); |
| 15 DECLARE_FLAG(bool, compiler_stats); | 22 DECLARE_FLAG(bool, compiler_stats); |
| 23 DECLARE_FLAG(bool, enable_type_checks); |
| 24 DECLARE_FLAG(bool, intrinsify); |
| 25 DECLARE_FLAG(bool, optimization_counter_threshold); |
| 26 DECLARE_FLAG(bool, print_ast); |
| 27 DECLARE_FLAG(bool, print_scopes); |
| 28 DECLARE_FLAG(bool, report_usage_count); |
| 29 DECLARE_FLAG(bool, trace_functions); |
| 30 |
| 31 |
| 32 class DeoptimizationStub : public ZoneAllocated { |
| 33 public: |
| 34 DeoptimizationStub(intptr_t deopt_id, |
| 35 intptr_t deopt_token_index, |
| 36 intptr_t try_index, |
| 37 DeoptReasonId reason) |
| 38 : deopt_id_(deopt_id), |
| 39 deopt_token_index_(deopt_token_index), |
| 40 try_index_(try_index), |
| 41 reason_(reason), |
| 42 registers_(2), |
| 43 entry_label_() {} |
| 44 |
| 45 void Push(Register reg) { registers_.Add(reg); } |
| 46 Label* entry_label() { return &entry_label_; } |
| 47 |
| 48 void GenerateCode(FlowGraphCompiler* compiler); |
| 49 |
| 50 private: |
| 51 const intptr_t deopt_id_; |
| 52 const intptr_t deopt_token_index_; |
| 53 const intptr_t try_index_; |
| 54 const DeoptReasonId reason_; |
| 55 GrowableArray<Register> registers_; |
| 56 Label entry_label_; |
| 57 |
| 58 DISALLOW_COPY_AND_ASSIGN(DeoptimizationStub); |
| 59 }; |
| 60 |
| 61 |
| 62 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { |
| 63 Assembler* assem = compiler->assembler(); |
| 64 #define __ assem-> |
| 65 __ Comment("Deopt stub for id %d", deopt_id_); |
| 66 __ Bind(entry_label()); |
| 67 for (intptr_t i = 0; i < registers_.length(); i++) { |
| 68 __ pushl(registers_[i]); |
| 69 } |
| 70 __ movl(EAX, Immediate(Smi::RawValue(reason_))); |
| 71 __ call(&StubCode::DeoptimizeLabel()); |
| 72 compiler->AddCurrentDescriptor(PcDescriptors::kOther, |
| 73 deopt_id_, |
| 74 deopt_token_index_, |
| 75 try_index_); |
| 76 #undef __ |
| 77 } |
| 78 |
| 79 |
| 80 FlowGraphCompiler::FlowGraphCompiler( |
| 81 Assembler* assembler, |
| 82 const ParsedFunction& parsed_function, |
| 83 const GrowableArray<BlockEntryInstr*>& block_order, |
| 84 bool is_optimizing) |
| 85 : FlowGraphVisitor(block_order), |
| 86 assembler_(assembler), |
| 87 parsed_function_(parsed_function), |
| 88 block_info_(block_order.length()), |
| 89 current_block_(NULL), |
| 90 pc_descriptors_list_(NULL), |
| 91 stackmap_builder_(NULL), |
| 92 exception_handlers_list_(NULL), |
| 93 deopt_stubs_(), |
| 94 is_optimizing_(is_optimizing) { |
| 95 } |
| 96 |
| 97 |
| 98 FlowGraphCompiler::~FlowGraphCompiler() { |
| 99 // BlockInfos are zone-allocated, so their destructors are not called. |
| 100 // Verify the labels explicitly here. |
| 101 for (int i = 0; i < block_info_.length(); ++i) { |
| 102 ASSERT(!block_info_[i]->label.IsLinked()); |
| 103 ASSERT(!block_info_[i]->label.HasNear()); |
| 104 } |
| 105 } |
| 106 |
| 107 |
| 108 void FlowGraphCompiler::InitCompiler() { |
| 109 pc_descriptors_list_ = new DescriptorList(); |
| 110 exception_handlers_list_ = new ExceptionHandlerList(); |
| 111 block_info_.Clear(); |
| 112 for (int i = 0; i < block_order_.length(); ++i) { |
| 113 block_info_.Add(new BlockInfo()); |
| 114 } |
| 115 } |
| 116 |
| 16 | 117 |
| 17 void FlowGraphCompiler::Bailout(const char* reason) { | 118 void FlowGraphCompiler::Bailout(const char* reason) { |
| 18 const char* kFormat = "FlowGraphCompiler Bailout: %s."; | 119 const char* kFormat = "FlowGraphCompiler Bailout: %s."; |
| 19 intptr_t len = OS::SNPrint(NULL, 0, kFormat, reason) + 1; | 120 intptr_t len = OS::SNPrint(NULL, 0, kFormat, reason) + 1; |
| 20 char* chars = reinterpret_cast<char*>( | 121 char* chars = reinterpret_cast<char*>( |
| 21 Isolate::Current()->current_zone()->Allocate(len)); | 122 Isolate::Current()->current_zone()->Allocate(len)); |
| 22 OS::SNPrint(chars, len, kFormat, reason); | 123 OS::SNPrint(chars, len, kFormat, reason); |
| 23 const Error& error = Error::Handle( | 124 const Error& error = Error::Handle( |
| 24 LanguageError::New(String::Handle(String::New(chars)))); | 125 LanguageError::New(String::Handle(String::New(chars)))); |
| 25 Isolate::Current()->long_jump_base()->Jump(1, error); | 126 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 26 } | 127 } |
| 27 | 128 |
| 28 | 129 |
| 130 // Uses current pc position and try-index. |
| 131 void FlowGraphCompiler::AddCurrentDescriptor(PcDescriptors::Kind kind, |
| 132 intptr_t cid, |
| 133 intptr_t token_index, |
| 134 intptr_t try_index) { |
| 135 pc_descriptors_list_->AddDescriptor(kind, |
| 136 assembler_->CodeSize(), |
| 137 cid, |
| 138 token_index, |
| 139 try_index); |
| 140 } |
| 141 |
| 142 #define __ assembler_-> |
| 143 |
| 144 void FlowGraphCompiler::IntrinsifyGetter() { |
| 145 // TOS: return address. |
| 146 // +1 : receiver. |
| 147 // Sequence node has one return node, its input is load field node. |
| 148 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); |
| 149 ASSERT(sequence_node.length() == 1); |
| 150 ASSERT(sequence_node.NodeAt(0)->IsReturnNode()); |
| 151 const ReturnNode& return_node = *sequence_node.NodeAt(0)->AsReturnNode(); |
| 152 ASSERT(return_node.value()->IsLoadInstanceFieldNode()); |
| 153 const LoadInstanceFieldNode& load_node = |
| 154 *return_node.value()->AsLoadInstanceFieldNode(); |
| 155 __ movl(EAX, Address(ESP, 1 * kWordSize)); |
| 156 __ movl(EAX, FieldAddress(EAX, load_node.field().Offset())); |
| 157 __ ret(); |
| 158 } |
| 159 |
| 160 |
| 161 void FlowGraphCompiler::IntrinsifySetter() { |
| 162 // TOS: return address. |
| 163 // +1 : value |
| 164 // +2 : receiver. |
| 165 // Sequence node has one store node and one return NULL node. |
| 166 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); |
| 167 ASSERT(sequence_node.length() == 2); |
| 168 ASSERT(sequence_node.NodeAt(0)->IsStoreInstanceFieldNode()); |
| 169 ASSERT(sequence_node.NodeAt(1)->IsReturnNode()); |
| 170 const StoreInstanceFieldNode& store_node = |
| 171 *sequence_node.NodeAt(0)->AsStoreInstanceFieldNode(); |
| 172 __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver. |
| 173 __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value. |
| 174 __ StoreIntoObject(EAX, FieldAddress(EAX, store_node.field().Offset()), EBX); |
| 175 const Immediate raw_null = |
| 176 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 177 __ movl(EAX, raw_null); |
| 178 __ ret(); |
| 179 } |
| 180 |
| 181 |
| 182 intptr_t FlowGraphCompiler::StackSize() const { |
| 183 return parsed_function_.stack_local_count() + |
| 184 parsed_function_.copied_parameter_count(); |
| 185 } |
| 186 |
| 187 |
| 188 bool FlowGraphCompiler::CanOptimize() { |
| 189 return |
| 190 !FLAG_report_usage_count && |
| 191 (FLAG_optimization_counter_threshold >= 0) && |
| 192 !Isolate::Current()->debugger()->IsActive(); |
| 193 } |
| 194 |
| 195 |
| 196 // Returns 'true' if code generation for this function is complete, i.e., |
| 197 // no fall-through to regular code is needed. |
| 198 bool FlowGraphCompiler::TryIntrinsify() { |
| 199 if (!CanOptimize()) return false; |
| 200 // Intrinsification skips arguments checks, therefore disable if in checked |
| 201 // mode. |
| 202 if (FLAG_intrinsify && !FLAG_trace_functions && !FLAG_enable_type_checks) { |
| 203 if ((parsed_function_.function().kind() == RawFunction::kImplicitGetter)) { |
| 204 IntrinsifyGetter(); |
| 205 return true; |
| 206 } |
| 207 if ((parsed_function_.function().kind() == RawFunction::kImplicitSetter)) { |
| 208 IntrinsifySetter(); |
| 209 return true; |
| 210 } |
| 211 } |
| 212 // Even if an intrinsified version of the function was successfully |
| 213 // generated, it may fall through to the non-intrinsified method body. |
| 214 if (!FLAG_trace_functions) { |
| 215 return Intrinsifier::Intrinsify(parsed_function_.function(), assembler_); |
| 216 } |
| 217 return false; |
| 218 } |
| 219 |
| 220 |
| 221 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, |
| 222 intptr_t token_index, |
| 223 intptr_t try_index, |
| 224 const RuntimeEntry& entry) { |
| 225 __ CallRuntime(entry); |
| 226 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_index, try_index); |
| 227 } |
| 228 |
| 229 |
| 230 void FlowGraphCompiler::CopyParameters() { |
| 231 Bailout("Copy Parameters"); |
| 232 } |
| 233 |
| 234 |
| 29 void FlowGraphCompiler::CompileGraph() { | 235 void FlowGraphCompiler::CompileGraph() { |
| 30 Bailout("CompileGraph"); | 236 InitCompiler(); |
| 237 if (TryIntrinsify()) { |
| 238 __ int3(); |
| 239 __ jmp(&StubCode::FixCallersTargetLabel()); |
| 240 return; |
| 241 } |
| 242 // Specialized version of entry code from CodeGenerator::GenerateEntryCode. |
| 243 const Function& function = parsed_function_.function(); |
| 244 |
| 245 const int parameter_count = function.num_fixed_parameters(); |
| 246 const int num_copied_params = parsed_function_.copied_parameter_count(); |
| 247 const int local_count = parsed_function_.stack_local_count(); |
| 248 AssemblerMacros::EnterDartFrame(assembler_, (StackSize() * kWordSize)); |
| 249 // We check the number of passed arguments when we have to copy them due to |
| 250 // the presence of optional named parameters. |
| 251 // No such checking code is generated if only fixed parameters are declared, |
| 252 // unless we are debug mode or unless we are compiling a closure. |
| 253 if (num_copied_params == 0) { |
| 254 #ifdef DEBUG |
| 255 const bool check_arguments = true; |
| 256 #else |
| 257 const bool check_arguments = function.IsClosureFunction(); |
| 258 #endif |
| 259 if (check_arguments) { |
| 260 // Check that num_fixed <= argc <= num_params. |
| 261 Label argc_in_range; |
| 262 // Total number of args is the first Smi in args descriptor array (EDX). |
| 263 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); |
| 264 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); |
| 265 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); |
| 266 if (function.IsClosureFunction()) { |
| 267 GenerateCallRuntime(AstNode::kNoId, |
| 268 function.token_index(), |
| 269 CatchClauseNode::kInvalidTryIndex, |
| 270 kClosureArgumentMismatchRuntimeEntry); |
| 271 } else { |
| 272 __ Stop("Wrong number of arguments"); |
| 273 } |
| 274 __ Bind(&argc_in_range); |
| 275 } |
| 276 } else { |
| 277 CopyParameters(); |
| 278 } |
| 279 // Initialize (non-argument) stack allocated locals to null. |
| 280 if (local_count > 0) { |
| 281 const Immediate raw_null = |
| 282 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 283 __ movl(EAX, raw_null); |
| 284 const int base = parsed_function_.first_stack_local_index(); |
| 285 for (int i = 0; i < local_count; ++i) { |
| 286 // Subtract index i (locals lie at lower addresses than EBP). |
| 287 __ movl(Address(EBP, (base - i) * kWordSize), EAX); |
| 288 } |
| 289 } |
| 290 |
| 291 // Generate stack overflow check. |
| 292 __ cmpl(ESP, |
| 293 Address::Absolute(Isolate::Current()->stack_limit_address())); |
| 294 Label no_stack_overflow; |
| 295 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); |
| 296 GenerateCallRuntime(AstNode::kNoId, |
| 297 function.token_index(), |
| 298 CatchClauseNode::kInvalidTryIndex, |
| 299 kStackOverflowRuntimeEntry); |
| 300 __ Bind(&no_stack_overflow); |
| 301 |
| 302 if (FLAG_print_scopes) { |
| 303 // Print the function scope (again) after generating the prologue in order |
| 304 // to see annotations such as allocation indices of locals. |
| 305 if (FLAG_print_ast) { |
| 306 // Second printing. |
| 307 OS::Print("Annotated "); |
| 308 } |
| 309 AstPrinter::PrintFunctionScope(parsed_function_); |
| 310 } |
| 311 |
| 312 VisitBlocks(); |
| 313 |
| 314 __ int3(); |
| 315 GenerateDeferredCode(); |
| 316 // Emit function patching code. This will be swapped with the first 5 bytes |
| 317 // at entry point. |
| 318 pc_descriptors_list_->AddDescriptor(PcDescriptors::kPatchCode, |
| 319 assembler_->CodeSize(), |
| 320 AstNode::kNoId, |
| 321 0, |
| 322 -1); |
| 323 __ jmp(&StubCode::FixCallersTargetLabel()); |
| 324 } |
| 325 |
| 326 |
| 327 void FlowGraphCompiler::GenerateDeferredCode() { |
| 328 for (intptr_t i = 0; i < deopt_stubs_.length(); i++) { |
| 329 deopt_stubs_[i]->GenerateCode(this); |
| 330 } |
| 331 } |
| 332 |
| 333 |
| 334 void FlowGraphCompiler::EmitComment(Instruction* instr) { |
| 335 char buffer[80]; |
| 336 BufferFormatter f(buffer, sizeof(buffer)); |
| 337 instr->PrintTo(&f); |
| 338 __ Comment("@%d: %s", instr->cid(), buffer); |
| 339 } |
| 340 |
| 341 |
| 342 void FlowGraphCompiler::BailoutOnInstruction(Instruction* instr) { |
| 343 char buffer[80]; |
| 344 BufferFormatter f(buffer, sizeof(buffer)); |
| 345 instr->PrintTo(&f); |
| 346 Bailout(buffer); |
| 347 } |
| 348 |
| 349 |
| 350 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { |
| 351 LocationSummary* locs = instr->locs(); |
| 352 ASSERT(locs != NULL); |
| 353 |
| 354 locs->AllocateRegisters(); |
| 355 |
| 356 // Load instruction inputs into allocated registers. |
| 357 for (intptr_t i = locs->input_count() - 1; i >= 0; i--) { |
| 358 Location loc = locs->in(i); |
| 359 ASSERT(loc.kind() == Location::kRegister); |
| 360 __ popl(loc.reg()); |
| 361 } |
| 362 } |
| 363 |
| 364 |
| 365 void FlowGraphCompiler::VisitBlocks() { |
| 366 for (intptr_t i = 0; i < block_order_.length(); ++i) { |
| 367 __ Comment("B%d", i); |
| 368 // Compile the block entry. |
| 369 current_block_ = block_order_[i]; |
| 370 Instruction* instr = current_block()->Accept(this); |
| 371 // Compile all successors until an exit, branch, or a block entry. |
| 372 while ((instr != NULL) && !instr->IsBlockEntry()) { |
| 373 if (FLAG_code_comments) EmitComment(instr); |
| 374 if (instr->locs() == NULL) { |
| 375 BailoutOnInstruction(instr); |
| 376 } else { |
| 377 EmitInstructionPrologue(instr); |
| 378 instr->EmitNativeCode(this); |
| 379 instr = instr->StraightLineSuccessor(); |
| 380 } |
| 381 } |
| 382 BlockEntryInstr* successor = |
| 383 (instr == NULL) ? NULL : instr->AsBlockEntry(); |
| 384 if (successor != NULL) { |
| 385 // Block ended with a "goto". We can fall through if it is the |
| 386 // next block in the list. Otherwise, we need a jump. |
| 387 if ((i == block_order_.length() - 1) || |
| 388 (block_order_[i + 1] != successor)) { |
| 389 __ jmp(&block_info_[successor->postorder_number()]->label); |
| 390 } |
| 391 } |
| 392 } |
| 31 } | 393 } |
| 32 | 394 |
| 33 | 395 |
| 34 void FlowGraphCompiler::FinalizePcDescriptors(const Code& code) { | 396 void FlowGraphCompiler::FinalizePcDescriptors(const Code& code) { |
| 35 UNIMPLEMENTED(); | 397 ASSERT(pc_descriptors_list_ != NULL); |
| 398 const PcDescriptors& descriptors = PcDescriptors::Handle( |
| 399 pc_descriptors_list_->FinalizePcDescriptors(code.EntryPoint())); |
| 400 descriptors.Verify(parsed_function_.function().is_optimizable()); |
| 401 code.set_pc_descriptors(descriptors); |
| 36 } | 402 } |
| 37 | 403 |
| 38 | 404 |
| 39 void FlowGraphCompiler::FinalizeStackmaps(const Code& code) { | 405 void FlowGraphCompiler::FinalizeStackmaps(const Code& code) { |
| 40 UNIMPLEMENTED(); | 406 if (stackmap_builder_ == NULL) { |
| 407 // The unoptimizing compiler has no stack maps. |
| 408 code.set_stackmaps(Array::Handle()); |
| 409 } else { |
| 410 // Finalize the stack map array and add it to the code object. |
| 411 code.set_stackmaps( |
| 412 Array::Handle(stackmap_builder_->FinalizeStackmaps(code))); |
| 413 } |
| 41 } | 414 } |
| 42 | 415 |
| 43 | 416 |
| 44 void FlowGraphCompiler::FinalizeVarDescriptors(const Code& code) { | 417 void FlowGraphCompiler::FinalizeVarDescriptors(const Code& code) { |
| 45 UNIMPLEMENTED(); | 418 const LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle( |
| 419 parsed_function_.node_sequence()->scope()->GetVarDescriptors()); |
| 420 code.set_var_descriptors(var_descs); |
| 46 } | 421 } |
| 47 | 422 |
| 48 | 423 |
| 49 void FlowGraphCompiler::FinalizeExceptionHandlers(const Code& code) { | 424 void FlowGraphCompiler::FinalizeExceptionHandlers(const Code& code) { |
| 50 UNIMPLEMENTED(); | 425 ASSERT(exception_handlers_list_ != NULL); |
| 426 const ExceptionHandlers& handlers = ExceptionHandlers::Handle( |
| 427 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); |
| 428 code.set_exception_handlers(handlers); |
| 51 } | 429 } |
| 52 | 430 |
| 53 | 431 |
| 54 void FlowGraphCompiler::FinalizeComments(const Code& code) { | 432 void FlowGraphCompiler::FinalizeComments(const Code& code) { |
| 55 UNIMPLEMENTED(); | 433 code.set_comments(assembler_->GetCodeComments()); |
| 56 } | 434 } |
| 57 | 435 |
| 436 #undef __ |
| 58 | 437 |
| 59 } // namespace dart | 438 } // namespace dart |
| 60 | 439 |
| 61 #endif // defined TARGET_ARCH_IA32 | 440 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |