Chromium Code Reviews| Index: runtime/vm/flow_graph_compiler_ia32.cc |
| =================================================================== |
| --- runtime/vm/flow_graph_compiler_ia32.cc (revision 9085) |
| +++ runtime/vm/flow_graph_compiler_ia32.cc (working copy) |
| @@ -9,19 +9,18 @@ |
| #include "lib/error.h" |
| #include "vm/ast_printer.h" |
| -#include "vm/compiler_stats.h" |
| #include "vm/il_printer.h" |
| #include "vm/locations.h" |
| #include "vm/object_store.h" |
| +#include "vm/parser.h" |
| #include "vm/stub_code.h" |
| namespace dart { |
| -DECLARE_FLAG(bool, compiler_stats); |
| +DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables."); |
| +DEFINE_FLAG(bool, trace_functions, false, "Trace entry of each function."); |
|
srdjan
2012/06/25 22:18:17
How about moving them into flow_graph_compiler.cc
regis
2012/06/25 22:22:59
Done.
|
| DECLARE_FLAG(bool, enable_type_checks); |
| DECLARE_FLAG(bool, print_ast); |
| -DECLARE_FLAG(bool, print_scopes); |
| -DECLARE_FLAG(bool, trace_functions); |
| void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { |
| @@ -44,403 +43,9 @@ |
| } |
| - |
| #define __ assembler()-> |
| -void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { |
| - // TOS: return address. |
| - // +1 : receiver. |
| - // Sequence node has one return node, its input is load field node. |
| - __ movl(EAX, Address(ESP, 1 * kWordSize)); |
| - __ movl(EAX, FieldAddress(EAX, offset)); |
| - __ ret(); |
| -} |
| - |
| -void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) { |
| - // TOS: return address. |
| - // +1 : value |
| - // +2 : receiver. |
| - __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver. |
| - __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value. |
| - __ StoreIntoObject(EAX, FieldAddress(EAX, offset), EBX); |
| - const Immediate raw_null = |
| - Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| - __ movl(EAX, raw_null); |
| - __ ret(); |
| -} |
| - |
| - |
| -void FlowGraphCompiler::GenerateInlinedMathSqrt(Label* done) { |
| - Label smi_to_double, double_op, call_method; |
| - __ movl(EAX, Address(ESP, 0)); |
| - __ testl(EAX, Immediate(kSmiTagMask)); |
| - __ j(ZERO, &smi_to_double); |
| - __ CompareClassId(EAX, kDouble, EBX); |
| - __ j(NOT_EQUAL, &call_method); |
| - __ movsd(XMM1, FieldAddress(EAX, Double::value_offset())); |
| - __ Bind(&double_op); |
| - __ sqrtsd(XMM0, XMM1); |
| - AssemblerMacros::TryAllocate(assembler_, |
| - double_class_, |
| - &call_method, |
| - EAX); // Result register. |
| - __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0); |
| - __ Drop(1); |
| - __ jmp(done); |
| - __ Bind(&smi_to_double); |
| - __ SmiUntag(EAX); |
| - __ cvtsi2sd(XMM1, EAX); |
| - __ jmp(&double_op); |
| - __ Bind(&call_method); |
| -} |
| - |
| - |
| -void FlowGraphCompiler::GenerateCall(intptr_t token_pos, |
| - intptr_t try_index, |
| - const ExternalLabel* label, |
| - PcDescriptors::Kind kind) { |
| - ASSERT(frame_register_allocator()->IsSpilled()); |
| - __ call(label); |
| - AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); |
| -} |
| - |
| - |
| -void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, |
| - intptr_t token_pos, |
| - intptr_t try_index, |
| - const RuntimeEntry& entry) { |
| - ASSERT(frame_register_allocator()->IsSpilled()); |
| - __ CallRuntime(entry); |
| - AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); |
| -} |
| - |
| - |
| -void FlowGraphCompiler::CopyParameters() { |
| - const Function& function = parsed_function().function(); |
| - const bool is_native_instance_closure = |
| - function.is_native() && function.IsImplicitInstanceClosureFunction(); |
| - LocalScope* scope = parsed_function().node_sequence()->scope(); |
| - const int num_fixed_params = function.num_fixed_parameters(); |
| - const int num_opt_params = function.num_optional_parameters(); |
| - int implicit_this_param_pos = is_native_instance_closure ? -1 : 0; |
| - ASSERT(parsed_function().first_parameter_index() == |
| - ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos); |
| - // Copy positional arguments. |
| - // Check that no fewer than num_fixed_params positional arguments are passed |
| - // in and that no more than num_params arguments are passed in. |
| - // Passed argument i at fp[1 + argc - i] |
| - // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i]. |
| - const int num_params = num_fixed_params + num_opt_params; |
| - |
| - // Total number of args is the first Smi in args descriptor array (EDX). |
| - __ movl(EBX, FieldAddress(EDX, Array::data_offset())); |
| - // Check that num_args <= num_params. |
| - Label wrong_num_arguments; |
| - __ cmpl(EBX, Immediate(Smi::RawValue(num_params))); |
| - __ j(GREATER, &wrong_num_arguments); |
| - // Number of positional args is the second Smi in descriptor array (EDX). |
| - __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); |
| - // Check that num_pos_args >= num_fixed_params. |
| - __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params))); |
| - __ j(LESS, &wrong_num_arguments); |
| - // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4. |
| - // Let EBX point to the last passed positional argument, i.e. to |
| - // fp[1 + num_args - (num_pos_args - 1)]. |
| - __ subl(EBX, ECX); |
| - __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize)); |
| - |
| - // Let EDI point to the last copied positional argument, i.e. to |
| - // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)]. |
| - const int index = |
| - ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos; |
| - // First copy captured receiver if function is an implicit native closure. |
| - if (is_native_instance_closure) { |
| - __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0))); |
| - __ movl(Address(EBP, (index * kWordSize)), EAX); |
| - } |
| - __ leal(EDI, Address(EBP, (index * kWordSize))); |
| - __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling. |
| - __ subl(EDI, ECX); |
| - __ SmiUntag(ECX); |
| - Label loop, loop_condition; |
| - __ jmp(&loop_condition, Assembler::kNearJump); |
| - // We do not use the final allocation index of the variable here, i.e. |
| - // scope->VariableAt(i)->index(), because captured variables still need |
| - // to be copied to the context that is not yet allocated. |
| - const Address argument_addr(EBX, ECX, TIMES_4, 0); |
| - const Address copy_addr(EDI, ECX, TIMES_4, 0); |
| - __ Bind(&loop); |
| - __ movl(EAX, argument_addr); |
| - __ movl(copy_addr, EAX); |
| - __ Bind(&loop_condition); |
| - __ decl(ECX); |
| - __ j(POSITIVE, &loop, Assembler::kNearJump); |
| - |
| - // Copy or initialize optional named arguments. |
| - Label all_arguments_processed; |
| - const Immediate raw_null = |
| - Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| - if (num_opt_params > 0) { |
| - // Start by alphabetically sorting the names of the optional parameters. |
| - LocalVariable** opt_param = new LocalVariable*[num_opt_params]; |
| - int* opt_param_position = new int[num_opt_params]; |
| - for (int pos = num_fixed_params; pos < num_params; pos++) { |
| - LocalVariable* parameter = scope->VariableAt(pos); |
| - const String& opt_param_name = parameter->name(); |
| - int i = pos - num_fixed_params; |
| - while (--i >= 0) { |
| - LocalVariable* param_i = opt_param[i]; |
| - const intptr_t result = opt_param_name.CompareTo(param_i->name()); |
| - ASSERT(result != 0); |
| - if (result > 0) break; |
| - opt_param[i + 1] = opt_param[i]; |
| - opt_param_position[i + 1] = opt_param_position[i]; |
| - } |
| - opt_param[i + 1] = parameter; |
| - opt_param_position[i + 1] = pos; |
| - } |
| - // Generate code handling each optional parameter in alphabetical order. |
| - // Total number of args is the first Smi in args descriptor array (EDX). |
| - __ movl(EBX, FieldAddress(EDX, Array::data_offset())); |
| - // Number of positional args is the second Smi in descriptor array (EDX). |
| - __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); |
| - __ SmiUntag(ECX); |
| - // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0]. |
| - __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); // EBX is Smi. |
| - // Let EDI point to the name/pos pair of the first named argument. |
| - __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize))); |
| - for (int i = 0; i < num_opt_params; i++) { |
| - // Handle this optional parameter only if k or fewer positional arguments |
| - // have been passed, where k is the position of this optional parameter in |
| - // the formal parameter list. |
| - Label load_default_value, assign_optional_parameter, next_parameter; |
| - const int param_pos = opt_param_position[i]; |
| - __ cmpl(ECX, Immediate(param_pos)); |
| - __ j(GREATER, &next_parameter, Assembler::kNearJump); |
| - // Check if this named parameter was passed in. |
| - __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the argument. |
| - __ CompareObject(EAX, opt_param[i]->name()); |
| - __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump); |
| - // Load EAX with passed-in argument at provided arg_pos, i.e. at |
| - // fp[1 + argc - arg_pos]. |
| - __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi. |
| - __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair. |
| - __ negl(EAX); |
| - Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi. |
| - __ movl(EAX, argument_addr); |
| - __ jmp(&assign_optional_parameter, Assembler::kNearJump); |
| - __ Bind(&load_default_value); |
| - // Load EAX with default argument at pos. |
| - const Object& value = Object::ZoneHandle( |
| - parsed_function().default_parameter_values().At( |
| - param_pos - num_fixed_params)); |
| - __ LoadObject(EAX, value); |
| - __ Bind(&assign_optional_parameter); |
| - // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos]. |
| - // We do not use the final allocation index of the variable here, i.e. |
| - // scope->VariableAt(i)->index(), because captured variables still need |
| - // to be copied to the context that is not yet allocated. |
| - intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex - |
| - param_pos + implicit_this_param_pos); |
| - const Address param_addr(EBP, (computed_param_pos * kWordSize)); |
| - __ movl(param_addr, EAX); |
| - __ Bind(&next_parameter); |
| - } |
| - delete[] opt_param; |
| - delete[] opt_param_position; |
| - // Check that EDI now points to the null terminator in the array descriptor. |
| - __ cmpl(Address(EDI, 0), raw_null); |
| - __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump); |
| - } else { |
| - ASSERT(is_native_instance_closure); |
| - __ jmp(&all_arguments_processed, Assembler::kNearJump); |
| - } |
| - |
| - __ Bind(&wrong_num_arguments); |
| - 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. |
| - __ addl(ESP, Immediate(StackSize() * kWordSize)); |
| - } |
| - if (function.IsClosureFunction()) { |
| - GenerateCallRuntime(AstNode::kNoId, |
| - 0, |
| - CatchClauseNode::kInvalidTryIndex, |
| - kClosureArgumentMismatchRuntimeEntry); |
| - } else { |
| - // Invoke noSuchMethod function. |
| - const int kNumArgsChecked = 1; |
| - ICData& ic_data = ICData::ZoneHandle(); |
| - ic_data = ICData::New(function, |
| - String::Handle(function.name()), |
| - AstNode::kNoId, |
| - kNumArgsChecked); |
| - __ LoadObject(ECX, ic_data); |
| - // EBP - 4 : PC marker, allows easy identification of RawInstruction obj. |
| - // EBP : points to previous frame pointer. |
| - // EBP + 4 : points to return address. |
| - // EBP + 8 : address of last argument (arg n-1). |
| - // ESP + 8 + 4*(n-1) : address of first argument (arg 0). |
| - // ECX : ic-data. |
| - // EDX : arguments descriptor array. |
| - __ call(&StubCode::CallNoSuchMethodFunctionLabel()); |
| - } |
| - |
| - if (FLAG_trace_functions) { |
| - __ pushl(EAX); // Preserve result. |
| - __ PushObject(Function::ZoneHandle(function.raw())); |
| - GenerateCallRuntime(AstNode::kNoId, |
| - 0, |
| - CatchClauseNode::kInvalidTryIndex, |
| - kTraceFunctionExitRuntimeEntry); |
| - __ popl(EAX); // Remove argument. |
| - __ popl(EAX); // Restore result. |
| - } |
| - __ LeaveFrame(); |
| - __ ret(); |
| - |
| - __ Bind(&all_arguments_processed); |
| - // Nullify originally passed arguments only after they have been copied and |
| - // checked, otherwise noSuchMethod would not see their original values. |
| - // This step can be skipped in case we decide that formal parameters are |
| - // implicitly final, since garbage collecting the unmodified value is not |
| - // an issue anymore. |
| - |
| - // EDX : arguments descriptor array. |
| - // Total number of args is the first Smi in args descriptor array (EDX). |
| - __ movl(ECX, FieldAddress(EDX, Array::data_offset())); |
| - __ SmiUntag(ECX); |
| - Label null_args_loop, null_args_loop_condition; |
| - __ jmp(&null_args_loop_condition, Assembler::kNearJump); |
| - const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize); |
| - __ Bind(&null_args_loop); |
| - __ movl(original_argument_addr, raw_null); |
| - __ Bind(&null_args_loop_condition); |
| - __ decl(ECX); |
| - __ j(POSITIVE, &null_args_loop, Assembler::kNearJump); |
| -} |
| - |
| - |
| -void FlowGraphCompiler::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 (EDX). |
| - __ 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_pos(), |
| - 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_pos(), |
| - 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 5 bytes |
| - // at entry point. |
| - pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, |
| - assembler()->CodeSize(), |
| - AstNode::kNoId, |
| - 0, |
| - -1); |
| - __ jmp(&StubCode::FixCallersTargetLabel()); |
| -} |
| - |
| - |
| -intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, |
| - const ICData& ic_data, |
| - const Array& arguments_descriptor, |
| - intptr_t argument_count) { |
| - __ LoadObject(ECX, ic_data); |
| - __ LoadObject(EDX, arguments_descriptor); |
| - |
| - __ call(target_label); |
| - const intptr_t descr_offset = assembler()->CodeSize(); |
| - __ Drop(argument_count); |
| - return descr_offset; |
| -} |
| - |
| - |
| -intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function, |
| - const Array& arguments_descriptor, |
| - intptr_t argument_count) { |
| - __ LoadObject(ECX, function); |
| - __ LoadObject(EDX, arguments_descriptor); |
| - __ call(&StubCode::CallStaticFunctionLabel()); |
| - const intptr_t descr_offset = assembler()->CodeSize(); |
| - __ Drop(argument_count); |
| - return descr_offset; |
| -} |
| - |
| - |
| // Fall through if bool_register contains null. |
| void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
| Label* is_true, |
| @@ -500,7 +105,8 @@ |
| // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| // type test is conclusive, otherwise fallthrough if a type test could not |
| // be completed. |
| -// EAX: instance (must survive), clobbers ECX, EDI |
| +// EAX: instance (must survive). |
| +// Clobbers ECX, EDI. |
| RawSubtypeTestCache* |
| FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| intptr_t cid, |
| @@ -522,6 +128,7 @@ |
| if (is_raw_type) { |
| const Register kClassIdReg = ECX; |
| // Dynamic type argument, check only classes. |
| + // List is a very common case. |
| __ LoadClassId(kClassIdReg, kInstanceReg); |
| if (!type_class.is_interface()) { |
| __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| @@ -576,7 +183,8 @@ |
| // Testing against an instantiated type with no arguments, without |
| // SubtypeTestCache. |
| -// EAX: instance to test against (preserved). Clobbers ECX, EDI. |
| +// EAX: instance to test against (preserved). |
| +// Clobbers ECX, EDI. |
| void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| intptr_t cid, |
| intptr_t token_pos, |
| @@ -612,8 +220,8 @@ |
| __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| __ j(EQUAL, is_instance_lbl); |
| } |
| + // Bool interface can be implemented only by core class Bool. |
| // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
| - // Bool interface can be implemented only by core class Bool. |
| if (type.IsBoolInterface()) { |
| __ cmpl(kClassIdReg, Immediate(kBool)); |
| __ j(EQUAL, is_instance_lbl); |
| @@ -643,11 +251,46 @@ |
| GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
| return; |
| } |
| + // Otherwise fallthrough. |
| } |
| +// Uses SubtypeTestCache to store instance class and result. |
| +// EAX: instance to test. |
| +// Clobbers EDI, ECX. |
| +// Immediate class test already done. |
| +// TODO(srdjan): Implement a quicker subtype check, as type test |
| +// arrays can grow too high, but they may be useful when optimizing |
| +// code (type-feedback). |
| +RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| + intptr_t cid, |
| + intptr_t token_pos, |
| + const Class& type_class, |
| + Label* is_instance_lbl, |
| + Label* is_not_instance_lbl) { |
| + const Register kInstanceReg = EAX; |
| + __ LoadClass(ECX, kInstanceReg, EDI); |
| + // ECX: instance class. |
| + // Check immediate superclass equality. |
| + __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); |
| + __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); |
| + __ CompareObject(EDI, type_class); |
| + __ j(EQUAL, is_instance_lbl); |
| + |
| + const Register kTypeArgumentsReg = kNoRegister; |
| + const Register kTempReg = EDI; |
| + return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
| + kInstanceReg, |
| + kTypeArgumentsReg, |
| + kTempReg, |
| + is_instance_lbl, |
| + is_not_instance_lbl); |
| +} |
| + |
| + |
| // Generates inlined check if 'type' is a type parameter or type itsef |
| -// EAX: instance (preserved). Clobbers EDX, EDI, ECX. |
| +// EAX: instance (preserved). |
| +// Clobbers EDX, EDI, ECX. |
| RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| intptr_t cid, |
| intptr_t token_pos, |
| @@ -670,7 +313,6 @@ |
| Label fall_through; |
| __ CompareClassId(EDX, kTypeArguments, EDI); |
| __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); |
| - |
| __ movl(EDI, |
| FieldAddress(EDX, TypeArguments::type_at_offset(type.Index()))); |
| // EDI: concrete type of type. |
| @@ -731,41 +373,10 @@ |
| } |
| -// Uses SubtypeTestCache to store instance class and result. |
| -// EAX: instance to test. Clobbers EDI, ECX. |
| -// Immediate class test already done. |
| -// TODO(srdjan): Implement a quicker subtype check, as type test |
| -// arrays can grow too high, but they may be useful when optimizing |
| -// code (type-feedback). |
| -RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( |
| - intptr_t cid, |
| - intptr_t token_pos, |
| - const Class& type_class, |
| - Label* is_instance_lbl, |
| - Label* is_not_instance_lbl) { |
| - const Register kInstanceReg = EAX; |
| - __ LoadClass(ECX, kInstanceReg, EDI); |
| - // ECX: instance class. |
| - // Check immediate superclass equality. |
| - __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); |
| - __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); |
| - __ CompareObject(EDI, type_class); |
| - __ j(EQUAL, is_instance_lbl); |
| - |
| - const Register kTypeArgumentsReg = kNoRegister; |
| - const Register kTempReg = EDI; |
| - return GenerateCallSubtypeTestStub(kTestTypeOneArg, |
| - kInstanceReg, |
| - kTypeArgumentsReg, |
| - kTempReg, |
| - is_instance_lbl, |
| - is_not_instance_lbl); |
| -} |
| - |
| - |
| // Inputs: |
| // - EAX: instance to test against (preserved). |
| // - EDX: optional instantiator type arguments (preserved). |
| +// Clobbers ECX, EDI. |
| // Returns: |
| // - preserved instance in EAX and optional instantiator type arguments in EDX. |
| // Note that this inlined code must be followed by the runtime_call code, as it |
| @@ -821,13 +432,14 @@ |
| // - EAX: object. |
| // - EDX: instantiator type arguments or raw_null. |
| // - ECX: instantiator or raw_null. |
| +// Clobbers ECX and EDX. |
| // Returns: |
| // - true or false in EAX. |
| void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, |
| - intptr_t token_pos, |
| - intptr_t try_index, |
| - const AbstractType& type, |
| - bool negate_result) { |
| + intptr_t token_pos, |
| + intptr_t try_index, |
| + const AbstractType& type, |
| + bool negate_result) { |
| ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| const Immediate raw_null = |
| @@ -848,7 +460,7 @@ |
| __ cmpl(EAX, raw_null); |
| __ j(EQUAL, &is_not_instance); |
| } |
| - // TODO(srdjan): Enable inlined checks. |
| + |
| // Generate inline instanceof test. |
| SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| test_cache = GenerateInlineInstanceof(cid, token_pos, type, |
| @@ -948,7 +560,6 @@ |
| return; |
| } |
| - // TODO(srdjan): Enable subtype test cache. |
| // Generate inline type check, linking to runtime call if not assignable. |
| SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, |
| @@ -995,6 +606,406 @@ |
| (locs->input_count() == instr->InputCount())); |
| } |
| + |
| +void FlowGraphCompiler::CopyParameters() { |
| + const Function& function = parsed_function().function(); |
| + const bool is_native_instance_closure = |
| + function.is_native() && function.IsImplicitInstanceClosureFunction(); |
| + LocalScope* scope = parsed_function().node_sequence()->scope(); |
| + const int num_fixed_params = function.num_fixed_parameters(); |
| + const int num_opt_params = function.num_optional_parameters(); |
| + int implicit_this_param_pos = is_native_instance_closure ? -1 : 0; |
| + ASSERT(parsed_function().first_parameter_index() == |
| + ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos); |
| + // Copy positional arguments. |
| + // Check that no fewer than num_fixed_params positional arguments are passed |
| + // in and that no more than num_params arguments are passed in. |
| + // Passed argument i at fp[1 + argc - i] |
| + // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i]. |
| + const int num_params = num_fixed_params + num_opt_params; |
| + |
| + // Total number of args is the first Smi in args descriptor array (EDX). |
| + __ movl(EBX, FieldAddress(EDX, Array::data_offset())); |
| + // Check that num_args <= num_params. |
| + Label wrong_num_arguments; |
| + __ cmpl(EBX, Immediate(Smi::RawValue(num_params))); |
| + __ j(GREATER, &wrong_num_arguments); |
| + // Number of positional args is the second Smi in descriptor array (EDX). |
| + __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); |
| + // Check that num_pos_args >= num_fixed_params. |
| + __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params))); |
| + __ j(LESS, &wrong_num_arguments); |
| + |
| + // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4. |
| + // Let EBX point to the last passed positional argument, i.e. to |
| + // fp[1 + num_args - (num_pos_args - 1)]. |
| + __ subl(EBX, ECX); |
| + __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize)); |
| + |
| + // Let EDI point to the last copied positional argument, i.e. to |
| + // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)]. |
| + const int index = |
| + ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos; |
| + // First copy captured receiver if function is an implicit native closure. |
| + if (is_native_instance_closure) { |
| + __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0))); |
| + __ movl(Address(EBP, (index * kWordSize)), EAX); |
| + } |
| + __ leal(EDI, Address(EBP, (index * kWordSize))); |
| + __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling. |
| + __ subl(EDI, ECX); |
| + __ SmiUntag(ECX); |
| + Label loop, loop_condition; |
| + __ jmp(&loop_condition, Assembler::kNearJump); |
| + // We do not use the final allocation index of the variable here, i.e. |
| + // scope->VariableAt(i)->index(), because captured variables still need |
| + // to be copied to the context that is not yet allocated. |
| + const Address argument_addr(EBX, ECX, TIMES_4, 0); |
| + const Address copy_addr(EDI, ECX, TIMES_4, 0); |
| + __ Bind(&loop); |
| + __ movl(EAX, argument_addr); |
| + __ movl(copy_addr, EAX); |
| + __ Bind(&loop_condition); |
| + __ decl(ECX); |
| + __ j(POSITIVE, &loop, Assembler::kNearJump); |
| + |
| + // Copy or initialize optional named arguments. |
| + const Immediate raw_null = |
| + Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| + Label all_arguments_processed; |
| + if (num_opt_params > 0) { |
| + // Start by alphabetically sorting the names of the optional parameters. |
| + LocalVariable** opt_param = new LocalVariable*[num_opt_params]; |
| + int* opt_param_position = new int[num_opt_params]; |
| + for (int pos = num_fixed_params; pos < num_params; pos++) { |
| + LocalVariable* parameter = scope->VariableAt(pos); |
| + const String& opt_param_name = parameter->name(); |
| + int i = pos - num_fixed_params; |
| + while (--i >= 0) { |
| + LocalVariable* param_i = opt_param[i]; |
| + const intptr_t result = opt_param_name.CompareTo(param_i->name()); |
| + ASSERT(result != 0); |
| + if (result > 0) break; |
| + opt_param[i + 1] = opt_param[i]; |
| + opt_param_position[i + 1] = opt_param_position[i]; |
| + } |
| + opt_param[i + 1] = parameter; |
| + opt_param_position[i + 1] = pos; |
| + } |
| + // Generate code handling each optional parameter in alphabetical order. |
| + // Total number of args is the first Smi in args descriptor array (EDX). |
| + __ movl(EBX, FieldAddress(EDX, Array::data_offset())); |
| + // Number of positional args is the second Smi in descriptor array (EDX). |
| + __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); |
| + __ SmiUntag(ECX); |
| + // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0]. |
| + __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); // EBX is Smi. |
| + // Let EDI point to the name/pos pair of the first named argument. |
| + __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize))); |
| + for (int i = 0; i < num_opt_params; i++) { |
| + // Handle this optional parameter only if k or fewer positional arguments |
| + // have been passed, where k is the position of this optional parameter in |
| + // the formal parameter list. |
| + Label load_default_value, assign_optional_parameter, next_parameter; |
| + const int param_pos = opt_param_position[i]; |
| + __ cmpl(ECX, Immediate(param_pos)); |
| + __ j(GREATER, &next_parameter, Assembler::kNearJump); |
| + // Check if this named parameter was passed in. |
| + __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the argument. |
| + __ CompareObject(EAX, opt_param[i]->name()); |
| + __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump); |
| + // Load EAX with passed-in argument at provided arg_pos, i.e. at |
| + // fp[1 + argc - arg_pos]. |
| + __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi. |
| + __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair. |
| + __ negl(EAX); |
| + Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi. |
| + __ movl(EAX, argument_addr); |
| + __ jmp(&assign_optional_parameter, Assembler::kNearJump); |
| + __ Bind(&load_default_value); |
| + // Load EAX with default argument at pos. |
| + const Object& value = Object::ZoneHandle( |
| + parsed_function().default_parameter_values().At( |
| + param_pos - num_fixed_params)); |
| + __ LoadObject(EAX, value); |
| + __ Bind(&assign_optional_parameter); |
| + // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos]. |
| + // We do not use the final allocation index of the variable here, i.e. |
| + // scope->VariableAt(i)->index(), because captured variables still need |
| + // to be copied to the context that is not yet allocated. |
| + intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex - |
| + param_pos + implicit_this_param_pos); |
| + const Address param_addr(EBP, (computed_param_pos * kWordSize)); |
| + __ movl(param_addr, EAX); |
| + __ Bind(&next_parameter); |
| + } |
| + delete[] opt_param; |
| + delete[] opt_param_position; |
| + // Check that EDI now points to the null terminator in the array descriptor. |
| + __ cmpl(Address(EDI, 0), raw_null); |
| + __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump); |
| + } else { |
| + ASSERT(is_native_instance_closure); |
| + __ jmp(&all_arguments_processed, Assembler::kNearJump); |
| + } |
| + |
| + __ Bind(&wrong_num_arguments); |
| + 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. |
| + __ addl(ESP, Immediate(StackSize() * kWordSize)); |
| + } |
| + if (function.IsClosureFunction()) { |
| + GenerateCallRuntime(AstNode::kNoId, |
| + 0, |
| + CatchClauseNode::kInvalidTryIndex, |
| + kClosureArgumentMismatchRuntimeEntry); |
| + } else { |
| + // Invoke noSuchMethod function. |
| + const int kNumArgsChecked = 1; |
| + ICData& ic_data = ICData::ZoneHandle(); |
| + ic_data = ICData::New(function, |
| + String::Handle(function.name()), |
| + AstNode::kNoId, |
| + kNumArgsChecked); |
| + __ LoadObject(ECX, ic_data); |
| + // EBP - 4 : PC marker, allows easy identification of RawInstruction obj. |
| + // EBP : points to previous frame pointer. |
| + // EBP + 4 : points to return address. |
| + // EBP + 8 : address of last argument (arg n-1). |
| + // ESP + 8 + 4*(n-1) : address of first argument (arg 0). |
| + // ECX : ic-data. |
| + // EDX : arguments descriptor array. |
| + __ call(&StubCode::CallNoSuchMethodFunctionLabel()); |
| + } |
| + |
| + if (FLAG_trace_functions) { |
| + __ pushl(EAX); // Preserve result. |
| + __ PushObject(Function::ZoneHandle(function.raw())); |
| + GenerateCallRuntime(AstNode::kNoId, |
| + 0, |
| + CatchClauseNode::kInvalidTryIndex, |
| + kTraceFunctionExitRuntimeEntry); |
| + __ popl(EAX); // Remove argument. |
| + __ popl(EAX); // Restore result. |
| + } |
| + __ LeaveFrame(); |
| + __ ret(); |
| + |
| + __ Bind(&all_arguments_processed); |
| + // Nullify originally passed arguments only after they have been copied and |
| + // checked, otherwise noSuchMethod would not see their original values. |
| + // This step can be skipped in case we decide that formal parameters are |
| + // implicitly final, since garbage collecting the unmodified value is not |
| + // an issue anymore. |
| + |
| + // EDX : arguments descriptor array. |
| + // Total number of args is the first Smi in args descriptor array (EDX). |
| + __ movl(ECX, FieldAddress(EDX, Array::data_offset())); |
| + __ SmiUntag(ECX); |
| + Label null_args_loop, null_args_loop_condition; |
| + __ jmp(&null_args_loop_condition, Assembler::kNearJump); |
| + const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize); |
| + __ Bind(&null_args_loop); |
| + __ movl(original_argument_addr, raw_null); |
| + __ Bind(&null_args_loop_condition); |
| + __ decl(ECX); |
| + __ j(POSITIVE, &null_args_loop, Assembler::kNearJump); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { |
| + // TOS: return address. |
| + // +1 : receiver. |
| + // Sequence node has one return node, its input is load field node. |
| + __ movl(EAX, Address(ESP, 1 * kWordSize)); |
| + __ movl(EAX, FieldAddress(EAX, offset)); |
| + __ ret(); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) { |
| + // TOS: return address. |
| + // +1 : value |
| + // +2 : receiver. |
| + // Sequence node has one store node and one return NULL node. |
| + __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver. |
| + __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value. |
| + __ StoreIntoObject(EAX, FieldAddress(EAX, offset), EBX); |
| + const Immediate raw_null = |
| + Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| + __ movl(EAX, raw_null); |
| + __ ret(); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::GenerateInlinedMathSqrt(Label* done) { |
| + Label smi_to_double, double_op, call_method; |
| + __ movl(EAX, Address(ESP, 0)); |
| + __ testl(EAX, Immediate(kSmiTagMask)); |
| + __ j(ZERO, &smi_to_double); |
| + __ CompareClassId(EAX, kDouble, EBX); |
| + __ j(NOT_EQUAL, &call_method); |
| + __ movsd(XMM1, FieldAddress(EAX, Double::value_offset())); |
| + __ Bind(&double_op); |
| + __ sqrtsd(XMM0, XMM1); |
| + AssemblerMacros::TryAllocate(assembler_, |
| + double_class_, |
| + &call_method, |
| + EAX); // Result register. |
| + __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0); |
| + __ Drop(1); |
| + __ jmp(done); |
| + __ Bind(&smi_to_double); |
| + __ SmiUntag(EAX); |
| + __ cvtsi2sd(XMM1, EAX); |
| + __ jmp(&double_op); |
| + __ Bind(&call_method); |
| +} |
| + |
| + |
| +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. |
| + __ 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 (EDX). |
| + __ 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_pos(), |
| + 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_pos(), |
| + 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 5 bytes |
| + // at entry point. |
| + pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, |
| + assembler()->CodeSize(), |
| + AstNode::kNoId, |
| + 0, |
| + -1); |
| + __ jmp(&StubCode::FixCallersTargetLabel()); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::GenerateCall(intptr_t token_pos, |
| + intptr_t try_index, |
| + const ExternalLabel* label, |
| + PcDescriptors::Kind kind) { |
| + ASSERT(frame_register_allocator()->IsSpilled()); |
| + __ call(label); |
| + AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); |
| +} |
| + |
| + |
| +void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, |
| + intptr_t token_pos, |
| + intptr_t try_index, |
| + const RuntimeEntry& entry) { |
| + ASSERT(frame_register_allocator()->IsSpilled()); |
| + __ CallRuntime(entry); |
| + AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); |
| +} |
| + |
| + |
| +intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, |
| + const ICData& ic_data, |
| + const Array& arguments_descriptor, |
| + intptr_t argument_count) { |
| + __ LoadObject(ECX, ic_data); |
| + __ LoadObject(EDX, arguments_descriptor); |
| + |
| + __ call(target_label); |
| + const intptr_t descr_offset = assembler()->CodeSize(); |
| + __ Drop(argument_count); |
| + return descr_offset; |
| +} |
| + |
| + |
| +intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function, |
| + const Array& arguments_descriptor, |
| + intptr_t argument_count) { |
| + __ LoadObject(ECX, function); |
| + __ LoadObject(EDX, arguments_descriptor); |
| + __ call(&StubCode::CallStaticFunctionLabel()); |
| + const intptr_t descr_offset = assembler()->CodeSize(); |
| + __ Drop(argument_count); |
| + return descr_offset; |
| +} |
| + |
| + |
| // Checks class id of instance against all 'class_ids'. Jump to 'deopt' label |
| // if no match or instance is Smi. |
| void FlowGraphCompiler::EmitClassChecksNoSmi(const ICData& ic_data, |
| @@ -1032,8 +1043,7 @@ |
| Label is_smi, done; |
| __ testl(reg, Immediate(kSmiTagMask)); |
| __ j(ZERO, &is_smi); |
| - __ LoadClassId(temp, reg); |
| - __ cmpl(temp, Immediate(kDouble)); |
| + __ CompareClassId(reg, kDouble, temp); |
| __ j(NOT_EQUAL, not_double_or_smi); |
| __ movsd(result, FieldAddress(reg, Double::value_offset())); |
| __ jmp(&done); |