Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(892)

Unified Diff: runtime/vm/flow_graph_compiler_ia32.cc

Issue 10636038: Minimize differences between ia32 and x64 sources to facilitate maintenance. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/flow_graph_compiler_ia32.h ('k') | runtime/vm/flow_graph_compiler_x64.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « runtime/vm/flow_graph_compiler_ia32.h ('k') | runtime/vm/flow_graph_compiler_x64.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698