Chromium Code Reviews| Index: runtime/vm/flow_graph_compiler_ia32.cc |
| =================================================================== |
| --- runtime/vm/flow_graph_compiler_ia32.cc (revision 8082) |
| +++ runtime/vm/flow_graph_compiler_ia32.cc (working copy) |
| @@ -7,13 +7,114 @@ |
| #include "vm/flow_graph_compiler.h" |
| +#include "vm/ast_printer.h" |
| #include "vm/compiler_stats.h" |
| +#include "vm/debugger.h" |
| +#include "vm/il_printer.h" |
| +#include "vm/intrinsifier.h" |
| +#include "vm/locations.h" |
| #include "vm/longjump.h" |
| +#include "vm/stub_code.h" |
| namespace dart { |
| +DECLARE_FLAG(bool, code_comments); |
| DECLARE_FLAG(bool, compiler_stats); |
| +DECLARE_FLAG(bool, enable_type_checks); |
| +DECLARE_FLAG(bool, intrinsify); |
| +DECLARE_FLAG(bool, optimization_counter_threshold); |
| +DECLARE_FLAG(bool, print_ast); |
| +DECLARE_FLAG(bool, print_scopes); |
| +DECLARE_FLAG(bool, report_usage_count); |
| +DECLARE_FLAG(bool, trace_functions); |
| + |
| +class DeoptimizationStub : public ZoneAllocated { |
| + public: |
| + DeoptimizationStub(intptr_t deopt_id, |
| + intptr_t deopt_token_index, |
| + intptr_t try_index, |
| + DeoptReasonId reason) |
| + : deopt_id_(deopt_id), |
| + deopt_token_index_(deopt_token_index), |
| + try_index_(try_index), |
| + reason_(reason), |
| + registers_(2), |
| + entry_label_() {} |
| + |
| + void Push(Register reg) { registers_.Add(reg); } |
| + Label* entry_label() { return &entry_label_; } |
| + |
| + void GenerateCode(FlowGraphCompiler* compiler); |
| + |
| + private: |
| + const intptr_t deopt_id_; |
| + const intptr_t deopt_token_index_; |
| + const intptr_t try_index_; |
| + const DeoptReasonId reason_; |
| + GrowableArray<Register> registers_; |
| + Label entry_label_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DeoptimizationStub); |
| +}; |
| + |
| + |
| +void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { |
| + Assembler* assem = compiler->assembler(); |
| +#define __ assem-> |
| + __ Comment("Deopt stub for id %d", deopt_id_); |
| + __ Bind(entry_label()); |
| + for (intptr_t i = 0; i < registers_.length(); i++) { |
| + __ pushl(registers_[i]); |
| + } |
| + __ movl(EAX, Immediate(Smi::RawValue(reason_))); |
| + __ call(&StubCode::DeoptimizeLabel()); |
| + compiler->AddCurrentDescriptor(PcDescriptors::kOther, |
| + deopt_id_, |
| + deopt_token_index_, |
| + try_index_); |
| +#undef __ |
| +} |
| + |
| + |
| +FlowGraphCompiler::FlowGraphCompiler( |
| + Assembler* assembler, |
| + const ParsedFunction& parsed_function, |
| + const GrowableArray<BlockEntryInstr*>& block_order, |
| + bool is_optimizing) |
| + : FlowGraphVisitor(block_order), |
| + assembler_(assembler), |
| + parsed_function_(parsed_function), |
| + block_info_(block_order.length()), |
| + current_block_(NULL), |
| + pc_descriptors_list_(NULL), |
| + stackmap_builder_(NULL), |
| + exception_handlers_list_(NULL), |
| + deopt_stubs_(), |
| + is_optimizing_(is_optimizing) { |
| +} |
| + |
| + |
| +FlowGraphCompiler::~FlowGraphCompiler() { |
| + // BlockInfos are zone-allocated, so their destructors are not called. |
| + // Verify the labels explicitly here. |
| + for (int i = 0; i < block_info_.length(); ++i) { |
| + ASSERT(!block_info_[i]->label.IsLinked()); |
| + ASSERT(!block_info_[i]->label.HasNear()); |
| + } |
| +} |
| + |
| + |
| +void FlowGraphCompiler::InitCompiler() { |
| + pc_descriptors_list_ = new DescriptorList(); |
| + exception_handlers_list_ = new ExceptionHandlerList(); |
| + block_info_.Clear(); |
| + for (int i = 0; i < block_order_.length(); ++i) { |
| + block_info_.Add(new BlockInfo()); |
| + } |
| +} |
| + |
| + |
| void FlowGraphCompiler::Bailout(const char* reason) { |
| const char* kFormat = "FlowGraphCompiler Bailout: %s."; |
| intptr_t len = OS::SNPrint(NULL, 0, kFormat, reason) + 1; |
| @@ -26,35 +127,313 @@ |
| } |
| +// Uses current pc position and try-index. |
| +void FlowGraphCompiler::AddCurrentDescriptor(PcDescriptors::Kind kind, |
| + intptr_t cid, |
| + intptr_t token_index, |
| + intptr_t try_index) { |
| + pc_descriptors_list_->AddDescriptor(kind, |
| + assembler_->CodeSize(), |
| + cid, |
| + token_index, |
| + try_index); |
| +} |
| + |
| +#define __ assembler_-> |
| + |
| +void FlowGraphCompiler::IntrinsifyGetter() { |
| + // TOS: return address. |
| + // +1 : receiver. |
| + // Sequence node has one return node, its input is load field node. |
| + const SequenceNode& sequence_node = *parsed_function_.node_sequence(); |
| + ASSERT(sequence_node.length() == 1); |
| + ASSERT(sequence_node.NodeAt(0)->IsReturnNode()); |
| + const ReturnNode& return_node = *sequence_node.NodeAt(0)->AsReturnNode(); |
| + ASSERT(return_node.value()->IsLoadInstanceFieldNode()); |
| + const LoadInstanceFieldNode& load_node = |
| + *return_node.value()->AsLoadInstanceFieldNode(); |
| + __ movl(EAX, Address(ESP, 1 * kWordSize)); |
| + __ movl(EAX, FieldAddress(EAX, load_node.field().Offset())); |
| + __ ret(); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::IntrinsifySetter() { |
| + // TOS: return address. |
| + // +1 : value |
| + // +2 : receiver. |
| + // Sequence node has one store node and one return NULL node. |
| + const SequenceNode& sequence_node = *parsed_function_.node_sequence(); |
| + ASSERT(sequence_node.length() == 2); |
| + ASSERT(sequence_node.NodeAt(0)->IsStoreInstanceFieldNode()); |
| + ASSERT(sequence_node.NodeAt(1)->IsReturnNode()); |
| + const StoreInstanceFieldNode& store_node = |
| + *sequence_node.NodeAt(0)->AsStoreInstanceFieldNode(); |
| + __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver. |
| + __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value. |
| + __ StoreIntoObject(EAX, FieldAddress(EAX, store_node.field().Offset()), EBX); |
| + const Immediate raw_null = |
| + Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| + __ movl(EAX, raw_null); |
| + __ ret(); |
| +} |
| + |
| + |
| +intptr_t FlowGraphCompiler::StackSize() const { |
| + return parsed_function_.stack_local_count() + |
| + parsed_function_.copied_parameter_count(); |
| +} |
| + |
| + |
| +bool FlowGraphCompiler::CanOptimize() { |
| + return |
| + !FLAG_report_usage_count && |
| + (FLAG_optimization_counter_threshold >= 0) && |
| + !Isolate::Current()->debugger()->IsActive(); |
| +} |
| + |
| + |
| +// Returns 'true' if code generation for this function is complete, i.e., |
| +// no fall-through to regular code is needed. |
| +bool FlowGraphCompiler::TryIntrinsify() { |
| + if (!CanOptimize()) return false; |
| + // Intrinsification skips arguments checks, therefore disable if in checked |
| + // mode. |
| + if (FLAG_intrinsify && !FLAG_trace_functions && !FLAG_enable_type_checks) { |
| + if ((parsed_function_.function().kind() == RawFunction::kImplicitGetter)) { |
| + IntrinsifyGetter(); |
| + return true; |
| + } |
| + if ((parsed_function_.function().kind() == RawFunction::kImplicitSetter)) { |
| + IntrinsifySetter(); |
| + return true; |
| + } |
| + } |
| + // Even if an intrinsified version of the function was successfully |
| + // generated, it may fall through to the non-intrinsified method body. |
| + if (!FLAG_trace_functions) { |
| + return Intrinsifier::Intrinsify(parsed_function_.function(), assembler_); |
| + } |
| + return false; |
| +} |
| + |
| + |
| +void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, |
| + intptr_t token_index, |
| + intptr_t try_index, |
| + const RuntimeEntry& entry) { |
| + __ CallRuntime(entry); |
| + AddCurrentDescriptor(PcDescriptors::kOther, cid, token_index, try_index); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::CopyParameters() { |
| + Bailout("Copy Parameters"); |
| +} |
| + |
| + |
| void FlowGraphCompiler::CompileGraph() { |
| - Bailout("CompileGraph"); |
| + InitCompiler(); |
| + if (TryIntrinsify()) { |
| + __ int3(); |
| + __ jmp(&StubCode::FixCallersTargetLabel()); |
| + return; |
| + } |
| + // Specialized version of entry code from CodeGenerator::GenerateEntryCode. |
| + const Function& function = parsed_function_.function(); |
| + |
| + const int parameter_count = function.num_fixed_parameters(); |
| + const int num_copied_params = parsed_function_.copied_parameter_count(); |
| + const int local_count = parsed_function_.stack_local_count(); |
| + AssemblerMacros::EnterDartFrame(assembler_, (StackSize() * kWordSize)); |
| + // We check the number of passed arguments when we have to copy them due to |
| + // the presence of optional named parameters. |
| + // No such checking code is generated if only fixed parameters are declared, |
| + // unless we are debug mode or unless we are compiling a closure. |
| + if (num_copied_params == 0) { |
| +#ifdef DEBUG |
| + const bool check_arguments = true; |
| +#else |
| + const bool check_arguments = function.IsClosureFunction(); |
| +#endif |
| + if (check_arguments) { |
| + // Check that num_fixed <= argc <= num_params. |
| + Label argc_in_range; |
| + // Total number of args is the first Smi in args descriptor array (R10). |
|
regis
2012/05/30 08:58:20
R10 -> EDX
srdjan
2012/05/30 16:20:39
Done.
|
| + __ movl(EAX, FieldAddress(EDX, Array::data_offset())); |
| + __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); |
| + __ j(EQUAL, &argc_in_range, Assembler::kNearJump); |
| + if (function.IsClosureFunction()) { |
| + GenerateCallRuntime(AstNode::kNoId, |
| + function.token_index(), |
| + CatchClauseNode::kInvalidTryIndex, |
| + kClosureArgumentMismatchRuntimeEntry); |
| + } else { |
| + __ Stop("Wrong number of arguments"); |
| + } |
| + __ Bind(&argc_in_range); |
| + } |
| + } else { |
| + CopyParameters(); |
| + } |
| + // Initialize (non-argument) stack allocated locals to null. |
| + if (local_count > 0) { |
| + const Immediate raw_null = |
| + Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| + __ movl(EAX, raw_null); |
| + const int base = parsed_function_.first_stack_local_index(); |
| + for (int i = 0; i < local_count; ++i) { |
| + // Subtract index i (locals lie at lower addresses than EBP). |
| + __ movl(Address(EBP, (base - i) * kWordSize), EAX); |
| + } |
| + } |
| + |
| + // Generate stack overflow check. |
| + __ cmpl(ESP, |
| + Address::Absolute(Isolate::Current()->stack_limit_address())); |
| + Label no_stack_overflow; |
| + __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); |
| + GenerateCallRuntime(AstNode::kNoId, |
| + function.token_index(), |
| + CatchClauseNode::kInvalidTryIndex, |
| + kStackOverflowRuntimeEntry); |
| + __ Bind(&no_stack_overflow); |
| + |
| + if (FLAG_print_scopes) { |
| + // Print the function scope (again) after generating the prologue in order |
| + // to see annotations such as allocation indices of locals. |
| + if (FLAG_print_ast) { |
| + // Second printing. |
| + OS::Print("Annotated "); |
| + } |
| + AstPrinter::PrintFunctionScope(parsed_function_); |
| + } |
| + |
| + VisitBlocks(); |
| + |
| + __ int3(); |
| + GenerateDeferredCode(); |
| + // Emit function patching code. This will be swapped with the first 13 bytes |
|
regis
2012/05/30 08:58:20
13 bytes? Isn't it just 5 bytes in 32-bit mode?
srdjan
2012/05/30 16:20:39
Done.
|
| + // at entry point. |
| + pc_descriptors_list_->AddDescriptor(PcDescriptors::kPatchCode, |
| + assembler_->CodeSize(), |
| + AstNode::kNoId, |
| + 0, |
| + -1); |
| + __ jmp(&StubCode::FixCallersTargetLabel()); |
| } |
| +void FlowGraphCompiler::GenerateDeferredCode() { |
| + for (intptr_t i = 0; i < deopt_stubs_.length(); i++) { |
| + deopt_stubs_[i]->GenerateCode(this); |
| + } |
| +} |
| + |
| + |
| +void FlowGraphCompiler::EmitComment(Instruction* instr) { |
| + char buffer[80]; |
| + BufferFormatter f(buffer, sizeof(buffer)); |
| + instr->PrintTo(&f); |
| + __ Comment("@%d: %s", instr->cid(), buffer); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::BailoutOnInstruction(Instruction* instr) { |
| + char buffer[80]; |
| + BufferFormatter f(buffer, sizeof(buffer)); |
| + instr->PrintTo(&f); |
| + Bailout(buffer); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { |
| + LocationSummary* locs = instr->locs(); |
| + ASSERT(locs != NULL); |
| + |
| + locs->AllocateRegisters(); |
| + |
| + // Load instruction inputs into allocated registers. |
| + for (intptr_t i = locs->input_count() - 1; i >= 0; i--) { |
| + Location loc = locs->in(i); |
| + ASSERT(loc.kind() == Location::kRegister); |
| + __ popl(loc.reg()); |
| + } |
| +} |
| + |
| + |
| +void FlowGraphCompiler::VisitBlocks() { |
| + for (intptr_t i = 0; i < block_order_.length(); ++i) { |
| + __ Comment("B%d", i); |
| + // Compile the block entry. |
| + current_block_ = block_order_[i]; |
| + Instruction* instr = current_block()->Accept(this); |
| + // Compile all successors until an exit, branch, or a block entry. |
| + while ((instr != NULL) && !instr->IsBlockEntry()) { |
| + if (FLAG_code_comments) EmitComment(instr); |
| + if (instr->locs() == NULL) { |
| + BailoutOnInstruction(instr); |
| + } else { |
| + EmitInstructionPrologue(instr); |
| + instr->EmitNativeCode(this); |
| + instr = instr->StraightLineSuccessor(); |
| + } |
| + } |
| + BlockEntryInstr* successor = |
| + (instr == NULL) ? NULL : instr->AsBlockEntry(); |
| + if (successor != NULL) { |
| + // Block ended with a "goto". We can fall through if it is the |
| + // next block in the list. Otherwise, we need a jump. |
| + if ((i == block_order_.length() - 1) || |
| + (block_order_[i + 1] != successor)) { |
| + __ jmp(&block_info_[successor->postorder_number()]->label); |
| + } |
| + } |
| + } |
| +} |
| + |
| + |
| void FlowGraphCompiler::FinalizePcDescriptors(const Code& code) { |
| - UNIMPLEMENTED(); |
| + ASSERT(pc_descriptors_list_ != NULL); |
| + const PcDescriptors& descriptors = PcDescriptors::Handle( |
| + pc_descriptors_list_->FinalizePcDescriptors(code.EntryPoint())); |
| + descriptors.Verify(parsed_function_.function().is_optimizable()); |
| + code.set_pc_descriptors(descriptors); |
| } |
| void FlowGraphCompiler::FinalizeStackmaps(const Code& code) { |
| - UNIMPLEMENTED(); |
| + if (stackmap_builder_ == NULL) { |
| + // The unoptimizing compiler has no stack maps. |
| + code.set_stackmaps(Array::Handle()); |
| + } else { |
| + // Finalize the stack map array and add it to the code object. |
| + code.set_stackmaps( |
| + Array::Handle(stackmap_builder_->FinalizeStackmaps(code))); |
| + } |
| } |
| void FlowGraphCompiler::FinalizeVarDescriptors(const Code& code) { |
| - UNIMPLEMENTED(); |
| + const LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle( |
| + parsed_function_.node_sequence()->scope()->GetVarDescriptors()); |
| + code.set_var_descriptors(var_descs); |
| } |
| void FlowGraphCompiler::FinalizeExceptionHandlers(const Code& code) { |
| - UNIMPLEMENTED(); |
| + ASSERT(exception_handlers_list_ != NULL); |
| + const ExceptionHandlers& handlers = ExceptionHandlers::Handle( |
| + exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); |
| + code.set_exception_handlers(handlers); |
| } |
| void FlowGraphCompiler::FinalizeComments(const Code& code) { |
| - UNIMPLEMENTED(); |
| + code.set_comments(assembler_->GetCodeComments()); |
| } |
| +#undef __ |
| } // namespace dart |