| Index: runtime/vm/flow_graph_compiler_ia32.cc
|
| ===================================================================
|
| --- runtime/vm/flow_graph_compiler_ia32.cc (revision 9088)
|
| +++ runtime/vm/flow_graph_compiler_ia32.cc (working copy)
|
| @@ -9,15 +9,14 @@
|
|
|
| #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);
|
| DECLARE_FLAG(bool, enable_type_checks);
|
| DECLARE_FLAG(bool, print_ast);
|
| DECLARE_FLAG(bool, print_scopes);
|
| @@ -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);
|
|
|