| Index: vm/opt_code_generator_ia32.cc
|
| ===================================================================
|
| --- vm/opt_code_generator_ia32.cc (revision 1093)
|
| +++ vm/opt_code_generator_ia32.cc (working copy)
|
| @@ -555,16 +555,22 @@
|
| }
|
|
|
|
|
| -static bool NodeHasOnlyClass(AstNode* node, const Class& cls) {
|
| +static bool AtIdNodeHasOnlyClass(AstNode* node, intptr_t id, const Class& cls) {
|
| ASSERT(node != NULL);
|
| ASSERT(!cls.IsNull());
|
| - const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
|
| - return (classes != NULL) &&
|
| - (classes->length() == 1) &&
|
| - ((*classes)[0]->raw() == cls.raw());
|
| + const ICData& ic_data = node->ICDataAtId(id);
|
| + if ((ic_data.NumberOfArgumentsChecked() != 1) ||
|
| + (ic_data.NumberOfChecks() != 1)) {
|
| + return false;
|
| + }
|
| + Class& target_cls = Class::Handle();
|
| + Function& target = Function::Handle();
|
| + ic_data.GetOneClassCheckAt(0, &target_cls, &target);
|
| + return target_cls.raw() == cls.raw();
|
| }
|
|
|
|
|
| +
|
| // Implement with slow case so that it can work both with Smi and Mint types.
|
| void OptimizingCodeGenerator::GenerateSmiShiftBinaryOp(BinaryOpNode* node) {
|
| ASSERT(node->kind() == Token::kSHL);
|
| @@ -1029,17 +1035,19 @@
|
| }
|
|
|
| ObjectStore* object_store = Isolate::Current()->object_store();
|
| - if (NodeHasOnlyClass(node, smi_class_)) {
|
| + if (AtIdNodeHasOnlyClass(node, node->id(), smi_class_)) {
|
| GenerateSmiBinaryOp(node);
|
| return;
|
| }
|
|
|
| - if (NodeHasOnlyClass(node, double_class_)) {
|
| + if (AtIdNodeHasOnlyClass(node, node->id(), double_class_)) {
|
| GenerateDoubleBinaryOp(node);
|
| return;
|
| }
|
|
|
| - if (NodeHasOnlyClass(node, Class::Handle(object_store->mint_class()))) {
|
| + if (AtIdNodeHasOnlyClass(node,
|
| + node->id(),
|
| + Class::Handle(object_store->mint_class()))) {
|
| GenerateMintBinaryOp(node, false);
|
| return;
|
| }
|
| @@ -1065,7 +1073,7 @@
|
| }
|
| const char* kOptMessage = "Inlines IncrOpLocal";
|
| ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
|
| - if (!NodeHasOnlyClass(node, smi_class_)) {
|
| + if (!AtIdNodeHasOnlyClass(node, node->id(), smi_class_)) {
|
| TraceNotOpt(node, kOptMessage);
|
| CodeGenerator::VisitIncrOpLocalNode(node);
|
| return;
|
| @@ -1099,6 +1107,85 @@
|
| }
|
|
|
|
|
| +void OptimizingCodeGenerator::VisitIncrOpInstanceFieldNode(
|
| + IncrOpInstanceFieldNode* node) {
|
| + ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
|
| + VisitLoadOne(node->receiver(), EBX);
|
| + __ pushl(EBX); // Duplicate receiver (preserve for setter).
|
| + const ICData& ic_data = node->ICDataAtId(node->id());
|
| + if (ic_data.NumberOfChecks() == 0) {
|
| + // Deoptimization point for this node is after receiver has been
|
| + // pushed twice on stack and before the getter (above) was executed.
|
| + DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EBX);
|
| + __ jmp(deopt_blob->label());
|
| + return;
|
| + }
|
| + InlineInstanceGetter(node,
|
| + node->getter_id(),
|
| + node->receiver(),
|
| + node->field_name(),
|
| + EBX);
|
| + // result is in EAX.
|
| + __ popl(EDX); // Get receiver.
|
| + const bool return_original_value = !node->prefix() && IsResultNeeded(node);
|
| + const Immediate one_value = Immediate(Smi::RawValue(1));
|
| + // EAX: Value.
|
| + // EDX: Receiver.
|
| + if (AtIdNodeHasOnlyClass(node, node->operator_id(), smi_class_)) {
|
| + // Deoptimization point for this node is after receiver has been
|
| + // pushed twice on stack and before the getter (above) was executed.
|
| + DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EDX, EDX);
|
| + if (return_original_value) {
|
| + // Preserve pre increment result.
|
| + __ movl(ECX, EAX);
|
| + }
|
| + __ testl(EAX, Immediate(kSmiTagMask));
|
| + __ j(NOT_ZERO, deopt_blob->label());
|
| + if (node->kind() == Token::kINCR) {
|
| + __ addl(EAX, one_value);
|
| + } else {
|
| + __ subl(EAX, one_value);
|
| + }
|
| + __ j(OVERFLOW, deopt_blob->label());
|
| + if (return_original_value) {
|
| + // Preserve as result.
|
| + __ pushl(ECX); // Preserve pre-increment value as result.
|
| + }
|
| + } else {
|
| + if (return_original_value) {
|
| + // Preserve as result.
|
| + __ pushl(EAX); // Preserve value as result.
|
| + }
|
| + __ pushl(EDX); // Preserve receiver.
|
| + __ pushl(EAX); // Left operand.
|
| + __ pushl(one_value); // Right operand.
|
| + const char* operator_name = (node->kind() == Token::kINCR) ? "+" : "-";
|
| + GenerateBinaryOperatorCall(node->operator_id(),
|
| + node->token_index(),
|
| + operator_name);
|
| + __ popl(EDX); // Restore receiver.
|
| + }
|
| + // EAX: Result of binary operation.
|
| + // EDX: receiver
|
| + if (IsResultNeeded(node) && node->prefix()) {
|
| + // Value stored into field is the result.
|
| + __ pushl(EAX);
|
| + }
|
| +
|
| + // TODO(srdjan): Inline instance setter.
|
| + __ pushl(EDX); // Receiver.
|
| + __ pushl(EAX); // Value.
|
| + // It is not necessary to generate a type test of the assigned value here,
|
| + // because the setter will check the type of its incoming arguments.
|
| + GenerateInstanceSetterCall(node->setter_id(),
|
| + node->token_index(),
|
| + node->field_name());
|
| +}
|
| +
|
| +
|
| +
|
| +
|
| +
|
| // Return offset of a field or -1 if field is not found.
|
| static intptr_t GetFieldOffset(const Class& field_class,
|
| const String& field_name) {
|
| @@ -1143,41 +1230,51 @@
|
|
|
|
|
| // Emits code for an instance getter that has one or more collected classes,
|
| -// all with the same target.
|
| +// all with the same target. Deoptimizes for Smi or unexpected class.
|
| +// EBX: loaded receiver.
|
| +// Result is returned in EAX.
|
| void OptimizingCodeGenerator::InlineInstanceGettersWithSameTarget(
|
| - InstanceGetterNode* node, const Function& target) {
|
| - const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
|
| - ASSERT(classes->length() > 0);
|
| - Label load_field;
|
| - VisitLoadOne(node->receiver(), EBX);
|
| + AstNode* node,
|
| + AstNode* receiver,
|
| + const String& field_name,
|
| + Register recv_reg) {
|
| + if (recv_reg != EBX) {
|
| + // TODO(srdjan): Do not hardwire register.
|
| + UNIMPLEMENTED();
|
| + }
|
| DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EBX);
|
| - if (NodeMayBeSmi(node->receiver())) {
|
| + if (NodeMayBeSmi(receiver)) {
|
| __ testl(EBX, Immediate(kSmiTagMask));
|
| __ j(ZERO, deopt_blob->label());
|
| }
|
| +
|
| __ movl(EAX, FieldAddress(EBX, Object::class_offset()));
|
| - const int num_classes = classes->length();
|
| - for (intptr_t i = 0; i < num_classes; i++) {
|
| - const Class& cls = *(*classes)[i];
|
| + const ICData& ic_data = node->ICDataAtId(node->id());
|
| + Function& target = Function::Handle();
|
| + Label load_field;
|
| + for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
|
| + Class& cls = Class::ZoneHandle();
|
| + ic_data.GetOneClassCheckAt(i, &cls, &target);
|
| __ CompareObject(EAX, cls);
|
| - if (i == (num_classes - 1)) {
|
| + if (i == (ic_data.NumberOfChecks() - 1)) {
|
| __ j(NOT_EQUAL, deopt_blob->label());
|
| } else {
|
| __ j(EQUAL, &load_field, Assembler::kNearJump);
|
| }
|
| }
|
| + Class& cls = Class::Handle();
|
| + ic_data.GetOneClassCheckAt(0, &cls, &target);
|
| +
|
| __ Bind(&load_field);
|
| -
|
| // EBX: receiver.
|
| if (target.kind() == RawFunction::kImplicitGetter) {
|
| TraceOpt(node, "Inlines instance getter with same target");
|
| - // Inlineable load field.
|
| - intptr_t field_offset = GetFieldOffset(*(*classes)[0],
|
| - node->field_name());
|
| + intptr_t field_offset = GetFieldOffset(cls, field_name);
|
| ASSERT(field_offset >= 0);
|
| __ movl(EAX, FieldAddress(EBX, field_offset));
|
| return;
|
| }
|
| +
|
| Recognizer::Kind recognized_kind = Recognizer::RecognizeKind(target);
|
| switch (recognized_kind) {
|
| case Recognizer::kObjectArrayLength: {
|
| @@ -1188,7 +1285,7 @@
|
| case Recognizer::kGrowableArrayLength: {
|
| TraceOpt(node, "Inlines GrowableObjectArray.length");
|
| intptr_t field_offset = GetFieldOffset(
|
| - *(*classes)[0],
|
| + cls,
|
| String::Handle(String::NewSymbol(kGrowableArrayLengthFieldName)));
|
| __ movl(EAX, FieldAddress(EBX, field_offset));
|
| return;
|
| @@ -1213,62 +1310,70 @@
|
| }
|
|
|
|
|
| -// TODO(srdjan): Implement for multiple getter targets.
|
| -// For every class inline its implicit getter, or call the instance getter.
|
| -void OptimizingCodeGenerator::VisitInstanceGetterNode(
|
| - InstanceGetterNode* node) {
|
| - const char* kMessage = "Inline instance getter";
|
| - const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
|
| - const String& getter_name =
|
| - String::Handle(Field::GetterName(node->field_name()));
|
| - if (FLAG_trace_optimization) {
|
| - OS::Print("Getter %s ", getter_name.ToCString());
|
| - }
|
| - if ((classes == NULL) || classes->is_empty()) {
|
| - TraceNotOpt(node, kMessage);
|
| - CodeGenerator::VisitInstanceGetterNode(node);
|
| - return;
|
| - }
|
| - // Collect all targets and identify if they are all inlineable and
|
| - // all same. Use 'targets' for case when not all targets are inlineable.
|
| - const intptr_t num_classes = classes->length();
|
| - GrowableArray<const Function*> targets(num_classes);
|
| - bool all_inlineable = true;
|
| - bool all_same_target = true;
|
| - for (intptr_t i = 0; i < num_classes; i++) {
|
| - const Class& cls = *(*classes)[i];
|
| - const int kNumArguments = 1;
|
| - const int kNumNamedArguments = 0;
|
| - const Function& target = Function::ZoneHandle(
|
| - Resolver::ResolveDynamicForReceiverClass(cls,
|
| - getter_name,
|
| - kNumArguments,
|
| - kNumNamedArguments));
|
| +// Return true if all targets in 'ic_data' point to same
|
| +// inlineable getter target.
|
| +bool OptimizingCodeGenerator::ICDataToSameInlineableInstanceGetter(
|
| + const ICData& ic_data) {
|
| + Function& prev_target = Function::Handle();
|
| + Function& target = Function::Handle();
|
| + Class& cls = Class::Handle();
|
| + for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
|
| + ic_data.GetOneClassCheckAt(i, &cls, &target);
|
| ASSERT(!target.IsNull());
|
| - targets.Add(&target);
|
| - if (targets[0]->raw() != target.raw()) {
|
| - all_same_target = false;
|
| + if (!prev_target.IsNull() && (prev_target.raw() != target.raw())) {
|
| + return false;
|
| }
|
| + prev_target = target.raw();
|
| if (!IsInlineableInstanceGetter(target)) {
|
| - all_inlineable = false;
|
| + return false;
|
| }
|
| }
|
| - // TODO(srdjan): implement other variants.
|
| - if (all_inlineable && all_same_target) {
|
| - InlineInstanceGettersWithSameTarget(node, *targets[0]);
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void OptimizingCodeGenerator::InlineInstanceGetter(AstNode* node,
|
| + intptr_t id,
|
| + AstNode* receiver,
|
| + const String& field_name,
|
| + Register recv_reg) {
|
| + if (ICDataToSameInlineableInstanceGetter(node->ICDataAtId(id))) {
|
| + InlineInstanceGettersWithSameTarget(node, receiver, field_name, recv_reg);
|
| } else {
|
| // TODO(srdjan): Inline access.
|
| - TraceNotOpt(node, kMessage);
|
| - node->receiver()->Visit(this);
|
| + __ pushl(recv_reg);
|
| const int kNumberOfArguments = 1;
|
| const Array& kNoArgumentNames = Array::Handle();
|
| GenerateCheckedInstanceCalls(node,
|
| - node->receiver(),
|
| + receiver,
|
| node->id(),
|
| node->token_index(),
|
| kNumberOfArguments,
|
| kNoArgumentNames);
|
| }
|
| +}
|
| +
|
| +
|
| +// TODO(srdjan): Implement for multiple getter targets.
|
| +// For every class inline its implicit getter, or call the instance getter.
|
| +void OptimizingCodeGenerator::VisitInstanceGetterNode(
|
| + InstanceGetterNode* node) {
|
| + const ICData& ic_data = node->ICDataAtId(node->id());
|
| + if (ic_data.NumberOfChecks() == 0) {
|
| + // No type feedback collected.
|
| + node->receiver()->Visit(this);
|
| + DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node);
|
| + __ jmp(deopt_blob->label());
|
| + return;
|
| + }
|
| +
|
| + VisitLoadOne(node->receiver(), EBX);
|
| + InlineInstanceGetter(node,
|
| + node->id(),
|
| + node->receiver(),
|
| + node->field_name(),
|
| + EBX);
|
| + // Result is in EAX.
|
| if (CodeGenerator::IsResultNeeded(node)) {
|
| __ pushl(EAX);
|
| }
|
| @@ -1770,12 +1875,12 @@
|
| return;
|
| }
|
|
|
| - if (NodeHasOnlyClass(node, smi_class_)) {
|
| + if (AtIdNodeHasOnlyClass(node, node->id(), smi_class_)) {
|
| if (GenerateSmiComparison(node)) {
|
| return;
|
| }
|
| // Fall through if condition is not supported.
|
| - } else if (NodeHasOnlyClass(node, double_class_)) {
|
| + } else if (AtIdNodeHasOnlyClass(node, node->id(), double_class_)) {
|
| // Double comparison
|
| if (GenerateDoubleComparison(node)) {
|
| return;
|
| @@ -1799,12 +1904,13 @@
|
| Class::ZoneHandle(object_store->array_class());
|
| const Class& immutable_object_array_class =
|
| Class::ZoneHandle(object_store->immutable_array_class());
|
| - if (NodeHasOnlyClass(node, object_array_class) ||
|
| - NodeHasOnlyClass(node, immutable_object_array_class)) {
|
| + if (AtIdNodeHasOnlyClass(node, node->id(), object_array_class) ||
|
| + AtIdNodeHasOnlyClass(node, node->id(), immutable_object_array_class)) {
|
| VisitLoadTwo(node->array(), node->index_expr(), EBX, EDX);
|
| DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EBX, EDX);
|
| - const Class& test_class = NodeHasOnlyClass(node, object_array_class) ?
|
| - object_array_class : immutable_object_array_class;
|
| + const Class& test_class =
|
| + AtIdNodeHasOnlyClass(node, node->id(), object_array_class) ?
|
| + object_array_class : immutable_object_array_class;
|
| // Type checks of array.
|
| __ testl(EBX, Immediate(kSmiTagMask)); // Deoptimize if Smi.
|
| __ j(ZERO, deopt_blob->label());
|
| @@ -1833,7 +1939,7 @@
|
| const Class& growable_array_class = Class::ZoneHandle(
|
| Library::Handle(Library::CoreImplLibrary()).
|
| LookupClass(growable_object_array_class_name));
|
| - if (NodeHasOnlyClass(node, growable_array_class)) {
|
| + if (AtIdNodeHasOnlyClass(node, node->id(), growable_array_class)) {
|
| const String& growable_array_length_field_name =
|
| String::Handle(String::NewSymbol(kGrowableArrayLengthFieldName));
|
| const String& growable_array_array_field_name =
|
| @@ -1882,7 +1988,7 @@
|
| ObjectStore* object_store = Isolate::Current()->object_store();
|
| const Class& object_array_class =
|
| Class::ZoneHandle(object_store->array_class());
|
| - if (NodeHasOnlyClass(node, object_array_class)) {
|
| + if (AtIdNodeHasOnlyClass(node, node->id(), object_array_class)) {
|
| VisitLoadTwo(node->index_expr(), node->value(), EBX, ECX);
|
| DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX, EBX, ECX);
|
| __ popl(EAX); // array.
|
| @@ -2198,7 +2304,7 @@
|
| Recognizer::KindToCString(recognized));
|
| }
|
| if ((recognized == Recognizer::kIntegerToDouble) &&
|
| - NodeHasOnlyClass(node, smi_class_)) {
|
| + AtIdNodeHasOnlyClass(node, node->id(), smi_class_)) {
|
| // TODO(srdjan): Check if we could use temporary double instead of
|
| // allocating a new object every time.
|
| const Code& stub =
|
| @@ -2217,7 +2323,7 @@
|
| }
|
|
|
| if ((recognized == Recognizer::kDoubleToDouble) &&
|
| - NodeHasOnlyClass(node, double_class_)) {
|
| + AtIdNodeHasOnlyClass(node, node->id(), double_class_)) {
|
| DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX);
|
| __ popl(EAX);
|
| CheckIfDoubleOrSmi(EAX, EBX, deopt_blob->label(), deopt_blob->label());
|
|
|