Chromium Code Reviews| Index: runtime/vm/flow_graph_compiler_mips.cc |
| =================================================================== |
| --- runtime/vm/flow_graph_compiler_mips.cc (revision 20565) |
| +++ runtime/vm/flow_graph_compiler_mips.cc (working copy) |
| @@ -7,10 +7,24 @@ |
| #include "vm/flow_graph_compiler.h" |
| -#include "vm/longjump.h" |
| +#include "lib/error.h" |
| +#include "vm/ast_printer.h" |
| +#include "vm/dart_entry.h" |
| +#include "vm/il_printer.h" |
| +#include "vm/locations.h" |
| +#include "vm/object_store.h" |
| +#include "vm/parser.h" |
| +#include "vm/stub_code.h" |
| +#include "vm/symbols.h" |
| namespace dart { |
| +DECLARE_FLAG(int, optimization_counter_threshold); |
| +DECLARE_FLAG(bool, print_ast); |
| +DECLARE_FLAG(bool, print_scopes); |
| +DECLARE_FLAG(bool, enable_type_checks); |
| + |
| + |
| FlowGraphCompiler::~FlowGraphCompiler() { |
| // BlockInfos are zone-allocated, so their destructors are not called. |
| // Verify the labels explicitly here. |
| @@ -31,6 +45,9 @@ |
| } |
| +#define __ assembler()-> |
| + |
| + |
| void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
| Label* is_true, |
| Label* is_false) { |
| @@ -128,12 +145,24 @@ |
| void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { |
| - UNIMPLEMENTED(); |
| + if (!is_optimizing()) { |
| + if (FLAG_enable_type_checks && instr->IsAssertAssignable()) { |
| + AssertAssignableInstr* assert = instr->AsAssertAssignable(); |
| + AddCurrentDescriptor(PcDescriptors::kDeoptBefore, |
| + assert->deopt_id(), |
| + assert->token_pos()); |
| + } |
| + AllocateRegistersLocally(instr); |
| + } |
| } |
| void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) { |
| - UNIMPLEMENTED(); |
| + if (is_optimizing()) return; |
| + Definition* defn = instr->AsDefinition(); |
| + if ((defn != NULL) && defn->is_used()) { |
| + __ Push(defn->locs()->out().reg()); |
| + } |
| } |
| @@ -153,12 +182,215 @@ |
| void FlowGraphCompiler::EmitFrameEntry() { |
| - UNIMPLEMENTED(); |
| + const Function& function = parsed_function().function(); |
| + if (CanOptimizeFunction() && function.is_optimizable()) { |
| + const bool can_optimize = !is_optimizing() || may_reoptimize(); |
| + const Register function_reg = T0; |
| + if (can_optimize) { |
| + Label next; |
| + // The pool pointer is not setup before entering the Dart frame. |
| + |
| + // We need to know the PC to find the pool pointer. |
| + __ GetPC(T2); |
| + |
| + // Preserve PP of caller. |
| + __ mov(T1, PP); |
| + |
| + // Temporarily setup pool pointer for this dart function. |
| + const intptr_t object_pool_pc_dist = |
| + Instructions::HeaderSize() - Instructions::object_pool_offset() + |
| + assembler()->CodeSize() - (3 * Instr::kInstrSize); |
| + __ lw(PP, Address(T2 /* PC - 12 */, -object_pool_pc_dist)); |
| + |
| + // Load function object from object pool. |
| + __ LoadObject(function_reg, function); // Uses PP. |
| + |
| + // Restore PP of caller. |
| + __ mov(PP, T1); |
| + } |
|
regis
2013/03/29 00:23:32
The sequence above is rather expensive. I am not h
zra
2013/03/29 17:10:44
I've inlined GetPC here, too. Yah, I guess if we d
|
| + // Patch point is after the eventually inlined function object. |
| + AddCurrentDescriptor(PcDescriptors::kEntryPatch, |
| + Isolate::kNoDeoptId, |
| + 0); // No token position. |
| + if (can_optimize) { |
| + // Reoptimization of optimized function is triggered by counting in |
| + // IC stubs, but not at the entry of the function. |
| + if (!is_optimizing()) { |
| + __ lw(T1, FieldAddress(function_reg, |
| + Function::usage_counter_offset())); |
| + __ addiu(T1, T1, Immediate(1)); |
| + __ sw(T1, FieldAddress(function_reg, |
| + Function::usage_counter_offset())); |
| + } else { |
| + __ lw(T1, FieldAddress(function_reg, |
| + Function::usage_counter_offset())); |
| + } |
| + |
| + // Skip Branch if T1 is less than the threshold. |
| + Label dont_branch; |
| + __ LoadImmediate(T2, FLAG_optimization_counter_threshold); |
| + __ sltu(T2, T1, T2); |
| + __ bgtz(T2, &dont_branch); |
| + |
| + ASSERT(function_reg == T0); |
| + __ Branch(&StubCode::OptimizeFunctionLabel()); |
| + |
| + __ Bind(&dont_branch); |
| + } |
| + } else { |
| + AddCurrentDescriptor(PcDescriptors::kEntryPatch, |
| + Isolate::kNoDeoptId, |
| + 0); // No token position. |
| + } |
| + __ Comment("Enter frame"); |
| + __ EnterDartFrame((StackSize() * kWordSize)); |
| } |
| +// Input parameters: |
| +// RA: return address. |
| +// SP: address of last argument. |
| +// FP: caller's frame pointer. |
| +// PP: caller's pool pointer. |
| +// S5: ic-data. |
| +// S4: arguments descriptor array. |
| +void FlowGraphCompiler::CompileGraph() { |
| + InitCompiler(); |
| + if (TryIntrinsify()) { |
| + // Although this intrinsified code will never be patched, it must satisfy |
| + // CodePatcher::CodeIsPatchable, which verifies that this code has a minimum |
| + // code size. |
| + __ break_(0); |
| + __ Branch(&StubCode::FixCallersTargetLabel()); |
| + return; |
| + } |
| -void FlowGraphCompiler::CompileGraph() { |
| - UNIMPLEMENTED(); |
| + EmitFrameEntry(); |
| + |
| + const Function& function = parsed_function().function(); |
| + |
| + const int num_fixed_params = function.num_fixed_parameters(); |
| + const int num_copied_params = parsed_function().num_copied_params(); |
| + const int num_locals = parsed_function().num_stack_locals(); |
| + |
| + // We check the number of passed arguments when we have to copy them due to |
| + // the presence of optional parameters. |
| + // No such checking code is generated if only fixed parameters are declared, |
| + // unless we are in debug mode or unless we are compiling a closure. |
| + LocalVariable* saved_args_desc_var = |
| + parsed_function().GetSavedArgumentsDescriptorVar(); |
| + if (num_copied_params == 0) { |
| +#ifdef DEBUG |
| + ASSERT(!parsed_function().function().HasOptionalParameters()); |
| + const bool check_arguments = true; |
| +#else |
| + const bool check_arguments = function.IsClosureFunction(); |
| +#endif |
| + if (check_arguments) { |
| + __ Comment("Check argument count"); |
| + // Check that exactly num_fixed arguments are passed in. |
| + Label correct_num_arguments, wrong_num_arguments; |
| + __ lw(T0, FieldAddress(S4, ArgumentsDescriptor::count_offset())); |
| + __ LoadImmediate(T1, Smi::RawValue(num_fixed_params)); |
| + __ bne(T0, T1, &wrong_num_arguments); |
| + |
| + __ lw(T1, FieldAddress(S4, |
| + ArgumentsDescriptor::positional_count_offset())); |
| + __ beq(T0, T1, &correct_num_arguments); |
| + __ Bind(&wrong_num_arguments); |
| + if (function.IsClosureFunction()) { |
| + if (StackSize() != 0) { |
| + // We need to unwind the space we reserved for locals and copied |
| + // parameters. The NoSuchMethodFunction stub does not expect to see |
| + // that area on the stack. |
| + __ addiu(SP, SP, Immediate(StackSize() * kWordSize)); |
| + } |
| + // The call below has an empty stackmap because we have just |
| + // dropped the spill slots. |
| + BitmapBuilder* empty_stack_bitmap = new BitmapBuilder(); |
| + |
| + // Invoke noSuchMethod function passing "call" as the function name. |
| + const int kNumArgsChecked = 1; |
| + const ICData& ic_data = ICData::ZoneHandle( |
| + ICData::New(function, Symbols::Call(), |
| + Isolate::kNoDeoptId, kNumArgsChecked)); |
| + __ LoadObject(S5, ic_data); |
| + // FP - 4 : saved PP, object pool pointer of caller. |
| + // FP + 0 : previous frame pointer. |
| + // FP + 4 : return address. |
| + // FP + 8 : PC marker, for easy identification of RawInstruction obj. |
| + // FP + 12: last argument (arg n-1). |
| + // SP + 0 : saved PP. |
| + // SP + 16 + 4*(n-1) : first argument (arg 0). |
| + // S5 : ic-data. |
| + // S4 : arguments descriptor array. |
| + __ BranchLink(&StubCode::CallNoSuchMethodFunctionLabel()); |
| + if (is_optimizing()) { |
| + stackmap_table_builder_->AddEntry(assembler()->CodeSize(), |
| + empty_stack_bitmap, |
| + 0); // No registers. |
| + } |
| + // The noSuchMethod call may return. |
| + __ LeaveDartFrame(); |
| + __ Ret(); |
| + } else { |
| + __ Stop("Wrong number of arguments"); |
| + } |
| + __ Bind(&correct_num_arguments); |
| + } |
| + // The arguments descriptor is never saved in the absence of optional |
| + // parameters, since any argument definition test would always yield true. |
| + ASSERT(saved_args_desc_var == NULL); |
| + } else { |
| + if (saved_args_desc_var != NULL) { |
| + __ Comment("Save arguments descriptor"); |
| + const Register kArgumentsDescriptorReg = S4; |
| + // The saved_args_desc_var is allocated one slot before the first local. |
| + const intptr_t slot = parsed_function().first_stack_local_index() + 1; |
| + // If the saved_args_desc_var is captured, it is first moved to the stack |
| + // and later to the context, once the context is allocated. |
| + ASSERT(saved_args_desc_var->is_captured() || |
| + (saved_args_desc_var->index() == slot)); |
| + __ sw(kArgumentsDescriptorReg, Address(FP, slot * kWordSize)); |
| + } |
| + CopyParameters(); |
| + } |
| + |
| + // In unoptimized code, initialize (non-argument) stack allocated slots to |
| + // null. This does not cover the saved_args_desc_var slot. |
| + if (!is_optimizing() && (num_locals > 0)) { |
| + __ Comment("Initialize spill slots"); |
| + const intptr_t slot_base = parsed_function().first_stack_local_index(); |
| + __ LoadImmediate(T0, reinterpret_cast<intptr_t>(Object::null())); |
| + for (intptr_t i = 0; i < num_locals; ++i) { |
| + // Subtract index i (locals lie at lower addresses than FP). |
| + __ sw(T0, Address(FP, (slot_base - i) * kWordSize)); |
| + } |
| + } |
| + |
| + 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(); |
| + |
| + __ break_(0); |
| + GenerateDeferredCode(); |
| + // Emit function patching code. This will be swapped with the first 5 bytes |
| + // at entry point. |
| + AddCurrentDescriptor(PcDescriptors::kPatchCode, |
| + Isolate::kNoDeoptId, |
| + 0); // No token position. |
| + __ Branch(&StubCode::FixCallersTargetLabel()); |
| + AddCurrentDescriptor(PcDescriptors::kLazyDeoptJump, |
| + Isolate::kNoDeoptId, |
| + 0); // No token position. |
| + __ Branch(&StubCode::DeoptimizeLazyLabel()); |
| } |
| @@ -183,7 +415,7 @@ |
| intptr_t deopt_id, |
| const RuntimeEntry& entry, |
| LocationSummary* locs) { |
| - UNIMPLEMENTED(); |
| + __ Unimplemented("call runtime"); |
| } |
| @@ -252,12 +484,51 @@ |
| void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) { |
| - UNIMPLEMENTED(); |
| + // TODO(vegorov): consider saving only caller save (volatile) registers. |
| + const intptr_t fpu_registers = locs->live_registers()->fpu_registers(); |
| + if (fpu_registers > 0) { |
| + UNIMPLEMENTED(); |
| + } |
| + |
| + // Store general purpose registers with the lowest register number at the |
| + // lowest address. |
| + const intptr_t cpu_registers = locs->live_registers()->cpu_registers(); |
| + ASSERT((cpu_registers & ~kAllCpuRegistersList) == 0); |
| + const int register_count = Utils::CountOneBits(cpu_registers); |
| + int registers_pushed = 0; |
| + |
| + __ addiu(SP, SP, Immediate(-register_count * kWordSize)); |
| + for (int i = 0; i < kNumberOfCpuRegisters; i++) { |
| + Register r = static_cast<Register>(i); |
| + if (locs->live_registers()->ContainsRegister(r)) { |
| + __ sw(r, Address(SP, registers_pushed * kWordSize)); |
| + registers_pushed++; |
| + } |
| + } |
| } |
| void FlowGraphCompiler::RestoreLiveRegisters(LocationSummary* locs) { |
| - UNIMPLEMENTED(); |
| + // General purpose registers have the lowest register number at the |
| + // lowest address. |
| + const intptr_t cpu_registers = locs->live_registers()->cpu_registers(); |
| + ASSERT((cpu_registers & ~kAllCpuRegistersList) == 0); |
| + const int register_count = Utils::CountOneBits(cpu_registers); |
| + int registers_popped = 0; |
| + |
| + for (int i = 0; i < kNumberOfCpuRegisters; i++) { |
| + Register r = static_cast<Register>(i); |
| + if (locs->live_registers()->ContainsRegister(r)) { |
| + __ lw(r, Address(SP, registers_popped * kWordSize)); |
| + registers_popped++; |
| + } |
| + } |
| + __ addiu(SP, SP, Immediate(register_count * kWordSize)); |
| + |
| + const intptr_t fpu_registers = locs->live_registers()->fpu_registers(); |
| + if (fpu_registers > 0) { |
| + UNIMPLEMENTED(); |
| + } |
| } |