| Index: vm/opt_code_generator_ia32.cc
|
| ===================================================================
|
| --- vm/opt_code_generator_ia32.cc (revision 2344)
|
| +++ vm/opt_code_generator_ia32.cc (working copy)
|
| @@ -305,13 +305,15 @@
|
| void OptimizingCodeGenerator::PrintCollectedClassesAtId(AstNode* node,
|
| intptr_t id) {
|
| const ICData& ic_data = node->ICDataAtId(id);
|
| - ASSERT(ic_data.NumberOfArgumentsChecked() == 1);
|
| - Function& target = Function::Handle();
|
| - Class& cls = Class::Handle();
|
| for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
|
| - ic_data.GetOneClassCheckAt(i, &cls, &target);
|
| - OS::Print("- %s -> %s\n", cls.ToCString(),
|
| - target.ToFullyQualifiedCString());
|
| + Function& target = Function::Handle();
|
| + GrowableArray<const Class*> classes;
|
| + ic_data.GetCheckAt(i, &classes, &target);
|
| + OS::Print("[");
|
| + for (intptr_t c = 0; c < classes.length(); c++) {
|
| + OS::Print("%s%s", (c > 0) ? ", " : "", classes[c]->ToCString());
|
| + }
|
| + OS::Print("] -> %s\n", target.ToFullyQualifiedCString());
|
| }
|
| }
|
|
|
| @@ -553,24 +555,55 @@
|
| }
|
|
|
|
|
| -static bool NodeHasBothClasses(AstNode* node,
|
| - const Class& cls1,
|
| - const Class& cls2) {
|
| +static bool NodeHasBothReceiverClasses(AstNode* node,
|
| + const Class& cls1,
|
| + const Class& cls2) {
|
| ASSERT(node != NULL);
|
| ASSERT(!cls1.IsNull() && !cls2.IsNull());
|
| + const ICData& ic_data = node->ICDataAtId(node->id());
|
| + bool cls1_found = false;
|
| + bool cls2_found = false;
|
| + for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
|
| + GrowableArray<const Class*> classes;
|
| + Function& target = Function::Handle();
|
| + ic_data.GetCheckAt(i, &classes, &target);
|
| + if (!classes.is_empty()) {
|
| + if (classes[0]->raw() == cls1.raw()) {
|
| + cls1_found = true;
|
| + }
|
| + if (classes[0]->raw() == cls2.raw()) {
|
| + cls2_found = true;
|
| + }
|
| + if (cls1_found && cls2_found) {
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + return false;
|
| +}
|
|
|
| - const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node);
|
| - if ((classes == NULL) || (classes->length() != 2)) {
|
| +
|
| +// Look only at the first class in all check groups.
|
| +static bool AtIdNodeHasReceiverClass(AstNode* node,
|
| + intptr_t id,
|
| + const Class& cls) {
|
| + ASSERT(node != NULL);
|
| + ASSERT(!cls.IsNull());
|
| + const ICData& ic_data = node->ICDataAtId(id);
|
| + if (ic_data.NumberOfChecks() == 0) {
|
| return false;
|
| }
|
| - if ((cls1.raw() != (*classes)[0]->raw()) &&
|
| - (cls1.raw() != (*classes)[1]->raw())) {
|
| - return false;
|
| + for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
|
| + GrowableArray<const Class*> classes;
|
| + Function& target = Function::Handle();
|
| + ic_data.GetCheckAt(i, &classes, &target);
|
| + if (classes.is_empty()) {
|
| + return false;
|
| + }
|
| + if (classes[0]->raw() != cls.raw()) {
|
| + return false;
|
| + }
|
| }
|
| - if ((cls2.raw() != (*classes)[0]->raw()) &&
|
| - (cls2.raw() != (*classes)[1]->raw())) {
|
| - return false;
|
| - }
|
| return true;
|
| }
|
|
|
| @@ -677,20 +710,43 @@
|
| (kind == Token::kBIT_OR) ||
|
| (kind == Token::kBIT_XOR)) {
|
| TraceOpt(node, kOptMessage);
|
| + // Check if both arguments are expected to be Smi.
|
| + const ICData& ic_data = node->ICDataAtId(node->id());
|
| + ASSERT(ic_data.NumberOfArgumentsChecked() == 2);
|
| + ASSERT(ic_data.NumberOfChecks() > 0);
|
| + Function& target = Function::Handle();
|
| + GrowableArray<const Class*> classes;
|
| + ic_data.GetCheckAt(0, &classes, &target);
|
| + const bool both_args_expected_smi =
|
| + (ic_data.NumberOfChecks() == 1) &&
|
| + (classes[0]->raw() == smi_class_.raw()) &&
|
| + (classes[1]->raw() == smi_class_.raw());
|
| +
|
| CodeGenInfo left_info(node->left());
|
| CodeGenInfo right_info(node->right());
|
| VisitLoadTwo(node->left(), node->right(), EAX, EDX);
|
| Label* overflow_label = NULL;
|
| Label two_smis, call_operator;
|
| - if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
|
| + if (both_args_expected_smi) {
|
| DeoptimizationBlob* deopt_blob =
|
| AddDeoptimizationBlob(node, ECX, EDX, kDeoptSmiBinaryOp);
|
| overflow_label = deopt_blob->label();
|
| __ movl(ECX, EAX); // Save if overflow (needs original value).
|
| - if (!left_info.IsClass(smi_class_) || !right_info.IsClass(smi_class_)) {
|
| - Register test_reg = left_info.IsClass(smi_class_) ? EDX : EAX;
|
| - __ testl(test_reg, Immediate(kSmiTagMask));
|
| +
|
| + if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
|
| + if (!left_info.IsClass(smi_class_) || !right_info.IsClass(smi_class_)) {
|
| + // One of the type is not known (statically) to be Smi. Check it.
|
| + Register test_reg = left_info.IsClass(smi_class_) ? EDX : EAX;
|
| + __ testl(test_reg, Immediate(kSmiTagMask));
|
| + __ j(NOT_ZERO, deopt_blob->label());
|
| + }
|
| + } else {
|
| + // Type feedback says both types are Smi, but static type analysis
|
| + // does not know if any of them is Smi, therefore check.
|
| + __ orl(EAX, EDX);
|
| + __ testl(EAX, Immediate(kSmiTagMask));
|
| __ j(NOT_ZERO, deopt_blob->label());
|
| + __ movl(EAX, ECX);
|
| }
|
| if (node->info() != NULL) {
|
| node->info()->set_is_class(&smi_class_);
|
| @@ -1060,24 +1116,24 @@
|
| }
|
|
|
| ObjectStore* object_store = Isolate::Current()->object_store();
|
| - if (AtIdNodeHasOnlyClass(node, node->id(), smi_class_)) {
|
| + if (AtIdNodeHasReceiverClass(node, node->id(), smi_class_)) {
|
| GenerateSmiBinaryOp(node);
|
| return;
|
| }
|
|
|
| - if (AtIdNodeHasOnlyClass(node, node->id(), double_class_)) {
|
| + if (AtIdNodeHasReceiverClass(node, node->id(), double_class_)) {
|
| GenerateDoubleBinaryOp(node);
|
| return;
|
| }
|
|
|
| - if (AtIdNodeHasOnlyClass(node,
|
| - node->id(),
|
| - Class::Handle(object_store->mint_class()))) {
|
| + if (AtIdNodeHasReceiverClass(node,
|
| + node->id(),
|
| + Class::Handle(object_store->mint_class()))) {
|
| GenerateMintBinaryOp(node, false);
|
| return;
|
| }
|
|
|
| - if (NodeHasBothClasses(node,
|
| + if (NodeHasBothReceiverClasses(node,
|
| smi_class_, Class::Handle(object_store->mint_class()))) {
|
| GenerateMintBinaryOp(node, true);
|
| return;
|
| @@ -1098,7 +1154,7 @@
|
| }
|
| const char* kOptMessage = "Inlines IncrOpLocal";
|
| ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
|
| - if (!AtIdNodeHasOnlyClass(node, node->id(), smi_class_)) {
|
| + if (!AtIdNodeHasReceiverClass(node, node->id(), smi_class_)) {
|
| TraceNotOpt(node, kOptMessage);
|
| CodeGenerator::VisitIncrOpLocalNode(node);
|
| return;
|
| @@ -1192,7 +1248,7 @@
|
| const Immediate one_value = Immediate(Smi::RawValue(1));
|
| // EAX: Value.
|
| // EDX: Receiver.
|
| - if (AtIdNodeHasOnlyClass(node, node->operator_id(), smi_class_)) {
|
| + if (AtIdNodeHasReceiverClass(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 =
|
| @@ -1971,12 +2027,12 @@
|
| return;
|
| }
|
|
|
| - if (AtIdNodeHasOnlyClass(node, node->id(), smi_class_)) {
|
| + if (AtIdNodeHasReceiverClass(node, node->id(), smi_class_)) {
|
| if (GenerateSmiComparison(node)) {
|
| return;
|
| }
|
| // Fall through if condition is not supported.
|
| - } else if (AtIdNodeHasOnlyClass(node, node->id(), double_class_)) {
|
| + } else if (AtIdNodeHasReceiverClass(node, node->id(), double_class_)) {
|
| // Double comparison
|
| if (GenerateDoubleComparison(node)) {
|
| return;
|
| @@ -2273,7 +2329,7 @@
|
| __ LoadObject(ECX, Array::ZoneHandle(ic_data.data()));
|
| __ LoadObject(EDX, ArgumentsDescriptor(num_args, optional_arguments_names));
|
| ExternalLabel target_label(
|
| - "InlineCache", StubCode::InlineCacheEntryPoint());
|
| + "InlineCache", StubCode::OneArgCheckInlineCacheEntryPoint());
|
|
|
| __ call(&target_label);
|
| AddCurrentDescriptor(PcDescriptors::kIcCall,
|
|
|