| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. |
| 6 #if defined(TARGET_ARCH_IA32) | 6 #if defined(TARGET_ARCH_IA32) |
| 7 | 7 |
| 8 #include "vm/opt_code_generator.h" | 8 #include "vm/opt_code_generator.h" |
| 9 | 9 |
| 10 #include "vm/assembler_macros.h" | 10 #include "vm/assembler_macros.h" |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 static const ZoneGrowableArray<const Class*>* | 271 static const ZoneGrowableArray<const Class*>* |
| 272 CollectedClassesAtNode(AstNode* node) { | 272 CollectedClassesAtNode(AstNode* node) { |
| 273 ZoneGrowableArray<const Class*>* result = | 273 ZoneGrowableArray<const Class*>* result = |
| 274 new ZoneGrowableArray<const Class*>(); | 274 new ZoneGrowableArray<const Class*>(); |
| 275 const ICData& ic_data = node->ICDataAtId(node->id()); | 275 const ICData& ic_data = node->ICDataAtId(node->id()); |
| 276 if (ic_data.NumberOfChecks() == 0) { | 276 if (ic_data.NumberOfChecks() == 0) { |
| 277 return result; | 277 return result; |
| 278 } | 278 } |
| 279 ASSERT(ic_data.NumberOfArgumentsChecked() == 1); | 279 ASSERT(ic_data.NumberOfArgumentsChecked() == 1); |
| 280 Function& target = Function::Handle(); | 280 Function& target = Function::Handle(); |
| 281 GrowableArray<const Class*> classes; | |
| 282 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) { | 281 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) { |
| 283 ic_data.GetCheckAt(i, &classes, &target); | 282 Class& cls = Class::ZoneHandle(); |
| 284 ASSERT(classes.length() == 1); | 283 ic_data.GetOneClassCheckAt(i, &cls, &target); |
| 285 result->Add(classes[0]); | 284 result->Add(&cls); |
| 286 } | 285 } |
| 287 return result; | 286 return result; |
| 288 } | 287 } |
| 289 | 288 |
| 290 | 289 |
| 291 // Debugging helper function. | 290 // Debugging helper function. |
| 292 void OptimizingCodeGenerator::PrintCollectedClasses(AstNode* node) { | 291 void OptimizingCodeGenerator::PrintCollectedClasses(AstNode* node) { |
| 293 const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node); | 292 const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node); |
| 294 for (intptr_t i = 0; i < classes->length(); i++) { | 293 for (intptr_t i = 0; i < classes->length(); i++) { |
| 295 OS::Print("- %s\n", (*classes)[i]->ToCString()); | 294 OS::Print("- %s\n", (*classes)[i]->ToCString()); |
| (...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 679 // Operator is called either if one of the arguments is not Smi or | 678 // Operator is called either if one of the arguments is not Smi or |
| 680 // if we hit an overflow in an arithmetic operation. | 679 // if we hit an overflow in an arithmetic operation. |
| 681 __ Bind(&call_operator); | 680 __ Bind(&call_operator); |
| 682 // Restore arguments on stack, and dispatch to operator, thus preventing | 681 // Restore arguments on stack, and dispatch to operator, thus preventing |
| 683 // deoptimization in case of smi/non-smi operations. At exit we do not | 682 // deoptimization in case of smi/non-smi operations. At exit we do not |
| 684 // know the result is Smi or not. | 683 // know the result is Smi or not. |
| 685 // TODO(srdjan): Handle type feedback for both arguments instead of for | 684 // TODO(srdjan): Handle type feedback for both arguments instead of for |
| 686 // receiver only, deoptimize if the type changes. | 685 // receiver only, deoptimize if the type changes. |
| 687 __ pushl(ECX); | 686 __ pushl(ECX); |
| 688 __ pushl(EDX); | 687 __ pushl(EDX); |
| 689 GenerateBinaryOperatorCall(node->id(), node->token_index(), node->Name()); | 688 GenerateBinaryOperatorCall(node->id(), |
| 689 node->token_index(), |
| 690 node->Name()); |
| 690 __ jmp(&done); | 691 __ jmp(&done); |
| 691 __ Bind(&two_smis); | 692 __ Bind(&two_smis); |
| 692 // Restore left operand. EAX will be 'destroyed', ECX holds the left | 693 // Restore left operand. EAX will be 'destroyed', ECX holds the left |
| 693 // argument, which may be needed for deoptimization. | 694 // argument, which may be needed for deoptimization. |
| 694 __ movl(EAX, ECX); | 695 __ movl(EAX, ECX); |
| 695 } | 696 } |
| 696 switch (kind) { | 697 switch (kind) { |
| 697 case Token::kADD: { | 698 case Token::kADD: { |
| 698 __ addl(EAX, EDX); | 699 __ addl(EAX, EDX); |
| 699 __ j(OVERFLOW, overflow_label); | 700 __ j(OVERFLOW, overflow_label); |
| (...skipping 549 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1249 all_same_target = false; | 1250 all_same_target = false; |
| 1250 } | 1251 } |
| 1251 if (!IsInlineableInstanceGetter(target)) { | 1252 if (!IsInlineableInstanceGetter(target)) { |
| 1252 all_inlineable = false; | 1253 all_inlineable = false; |
| 1253 } | 1254 } |
| 1254 } | 1255 } |
| 1255 // TODO(srdjan): implement other variants. | 1256 // TODO(srdjan): implement other variants. |
| 1256 if (all_inlineable && all_same_target) { | 1257 if (all_inlineable && all_same_target) { |
| 1257 InlineInstanceGettersWithSameTarget(node, *targets[0]); | 1258 InlineInstanceGettersWithSameTarget(node, *targets[0]); |
| 1258 } else { | 1259 } else { |
| 1260 // TODO(srdjan): Inline access. |
| 1259 TraceNotOpt(node, kMessage); | 1261 TraceNotOpt(node, kMessage); |
| 1260 node->receiver()->Visit(this); | 1262 node->receiver()->Visit(this); |
| 1261 GenerateInstanceGetterCall(node->id(), | 1263 const int kNumberOfArguments = 1; |
| 1262 node->token_index(), | 1264 const Array& kNoArgumentNames = Array::Handle(); |
| 1263 node->field_name()); | 1265 GenerateCheckedInstanceCalls(node, |
| 1266 node->receiver(), |
| 1267 node->id(), |
| 1268 node->token_index(), |
| 1269 kNumberOfArguments, |
| 1270 kNoArgumentNames); |
| 1264 } | 1271 } |
| 1265 if (CodeGenerator::IsResultNeeded(node)) { | 1272 if (CodeGenerator::IsResultNeeded(node)) { |
| 1266 __ pushl(EAX); | 1273 __ pushl(EAX); |
| 1267 } | 1274 } |
| 1268 } | 1275 } |
| 1269 | 1276 |
| 1270 | 1277 |
| 1271 // The call to the instance setter implements the assignment to a field. | 1278 // The call to the instance setter implements the assignment to a field. |
| 1272 // The result of the assignment to a field is the value being stored. | 1279 // The result of the assignment to a field is the value being stored. |
| 1273 void OptimizingCodeGenerator::VisitInstanceSetterNode( | 1280 void OptimizingCodeGenerator::VisitInstanceSetterNode( |
| (...skipping 653 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1927 __ Bind(&false_label); | 1934 __ Bind(&false_label); |
| 1928 node->false_branch()->Visit(this); | 1935 node->false_branch()->Visit(this); |
| 1929 __ Bind(&done); | 1936 __ Bind(&done); |
| 1930 } else { | 1937 } else { |
| 1931 __ Bind(&false_label); | 1938 __ Bind(&false_label); |
| 1932 } | 1939 } |
| 1933 __ Bind(&done); | 1940 __ Bind(&done); |
| 1934 } | 1941 } |
| 1935 | 1942 |
| 1936 | 1943 |
| 1937 // Return index where 'cls' is contained within 'classes' or -1 if not found. | 1944 void OptimizingCodeGenerator::GenerateDirectCall( |
| 1938 static intptr_t IndexOfClassInArray( | 1945 intptr_t node_id, |
| 1939 const ZoneGrowableArray<const Class*>& classes, | 1946 intptr_t token_index, |
| 1940 const Class& cls) { | 1947 Function& target, |
| 1941 for (int i = 0; i < classes.length(); i++) { | 1948 intptr_t arg_count, |
| 1942 if (classes[i]->raw() == cls.raw()) { | 1949 const Array& optional_argument_names) { |
| 1943 return i; | |
| 1944 } | |
| 1945 } | |
| 1946 return -1; | |
| 1947 } | |
| 1948 | |
| 1949 | |
| 1950 // Use static call pattern to call an instance method directly. | |
| 1951 void OptimizingCodeGenerator::GenerateDirectInstanceCall( | |
| 1952 InstanceCallNode* node, const Class& cls) { | |
| 1953 ASSERT(node != NULL); | |
| 1954 ASSERT(!cls.IsNull()); | |
| 1955 intptr_t arg_count = node->arguments()->length() + 1; | |
| 1956 const int num_named_arguments = node->arguments()->names().IsNull() ? | |
| 1957 0 : node->arguments()->names().Length(); | |
| 1958 const Function& target = Function::ZoneHandle( | |
| 1959 Resolver::ResolveDynamicForReceiverClass( | |
| 1960 cls, node->function_name(), arg_count, num_named_arguments)); | |
| 1961 ASSERT(!target.IsNull()); | 1950 ASSERT(!target.IsNull()); |
| 1962 const Code& code = Code::Handle(target.code()); | 1951 const Code& code = Code::Handle(target.code()); |
| 1963 ASSERT(!code.IsNull()); | 1952 ASSERT(!code.IsNull()); |
| 1964 ExternalLabel target_label("DirectInstanceCall", code.EntryPoint()); | 1953 ExternalLabel target_label("DirectInstanceCall", code.EntryPoint()); |
| 1965 | 1954 |
| 1966 __ LoadObject(ECX, target); | 1955 __ LoadObject(ECX, target); |
| 1967 __ LoadObject(EDX, ArgumentsDescriptor(arg_count, | 1956 __ LoadObject(EDX, ArgumentsDescriptor(arg_count, optional_argument_names)); |
| 1968 node->arguments()->names())); | |
| 1969 | |
| 1970 __ call(&target_label); | 1957 __ call(&target_label); |
| 1971 AddCurrentDescriptor(PcDescriptors::kOther, node->id(), node->token_index()); | 1958 AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_index); |
| 1972 __ addl(ESP, Immediate(arg_count * kWordSize)); | 1959 __ addl(ESP, Immediate(arg_count * kWordSize)); |
| 1973 } | 1960 } |
| 1974 | 1961 |
| 1975 | 1962 |
| 1976 // Using collected type feedback, inline class checks and call the targets | 1963 // Use ICData in 'node' to issues checks and calls. |
| 1977 // directly instead of via inline cache stub. TODO(srdjan): Use type propagation | 1964 void OptimizingCodeGenerator::GenerateCheckedInstanceCalls( |
| 1978 // and CHA to eliminate checks. | 1965 AstNode* node, |
| 1979 // Return false if no code was generated (because no type feedback was found). | 1966 AstNode* receiver, |
| 1980 bool OptimizingCodeGenerator::GenerateCheckedInstanceCalls( | 1967 intptr_t node_id, |
| 1981 InstanceCallNode* node) { | 1968 intptr_t token_index, |
| 1982 const ZoneGrowableArray<const Class*>* classes = CollectedClassesAtNode(node); | 1969 intptr_t num_args, |
| 1983 if ((classes == NULL) || classes->is_empty()) { | 1970 const Array& optional_argument_names) { |
| 1984 return false; | 1971 ASSERT(node != NULL); |
| 1972 ASSERT(receiver != NULL); |
| 1973 ASSERT(num_args > 0); |
| 1974 DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node); |
| 1975 const ICData& ic_data = node->ICDataAtId(node_id); |
| 1976 if (ic_data.NumberOfChecks() == 0) { |
| 1977 // No type feedback means node was never executed. |
| 1978 __ jmp(deopt_blob->label()); |
| 1979 return; |
| 1980 } |
| 1981 ASSERT(ic_data.NumberOfArgumentsChecked() == 1); |
| 1982 |
| 1983 // First test for Smi. Null object will cause deoptimization. |
| 1984 intptr_t smi_class_index = -1; |
| 1985 Class& smi_test_class = Class::Handle(); |
| 1986 Function& smi_target = Function::ZoneHandle(); |
| 1987 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) { |
| 1988 ic_data.GetOneClassCheckAt(i, &smi_test_class, &smi_target); |
| 1989 if (smi_test_class.raw() == smi_class_.raw()) { |
| 1990 smi_class_index = i; |
| 1991 break; |
| 1992 } |
| 1985 } | 1993 } |
| 1986 | 1994 |
| 1987 const int arg_count = node->arguments()->length() + 1; // With receiver. | |
| 1988 DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node); | |
| 1989 ASSERT(arg_count > 0); | |
| 1990 __ movl(EAX, Address(ESP, (arg_count - 1) * kWordSize)); // Load receiver. | |
| 1991 | |
| 1992 Label done; | 1995 Label done; |
| 1993 // If needed, Smi test must come first. | 1996 __ movl(EAX, Address(ESP, (num_args - 1) * kWordSize)); // Load receiver. |
| 1994 const intptr_t smi_class_index = IndexOfClassInArray(*classes, smi_class_); | |
| 1995 if (smi_class_index >= 0) { | 1997 if (smi_class_index >= 0) { |
| 1996 // Smi test is needed. | 1998 // Smi test is needed. |
| 1997 __ testl(EAX, Immediate(kSmiTagMask)); | 1999 __ testl(EAX, Immediate(kSmiTagMask)); |
| 1998 if (classes->length() == 1) { | 2000 if (ic_data.NumberOfChecks() == 1) { |
| 1999 // Only the Smi test. | 2001 // Only the Smi test. |
| 2000 __ j(NOT_ZERO, deopt_blob->label()); | 2002 __ j(NOT_ZERO, deopt_blob->label()); |
| 2001 GenerateDirectInstanceCall(node, smi_class_); | 2003 GenerateDirectCall(node_id, |
| 2002 return true; | 2004 token_index, |
| 2005 smi_target, |
| 2006 num_args, |
| 2007 optional_argument_names); |
| 2008 return; |
| 2003 } | 2009 } |
| 2004 Label not_smi; | 2010 Label not_smi; |
| 2005 __ j(NOT_ZERO, ¬_smi); | 2011 __ j(NOT_ZERO, ¬_smi); |
| 2006 GenerateDirectInstanceCall(node, smi_class_); | 2012 GenerateDirectCall(node_id, |
| 2013 token_index, |
| 2014 smi_target, |
| 2015 num_args, |
| 2016 optional_argument_names); |
| 2007 __ jmp(&done); | 2017 __ jmp(&done); |
| 2008 __ Bind(¬_smi); // Continue with other test below. | 2018 __ Bind(¬_smi); // Continue with other test below. |
| 2009 } else if (NodeMayBeSmi(node->receiver())) { | 2019 } else if (NodeMayBeSmi(receiver)) { |
| 2010 __ testl(EAX, Immediate(kSmiTagMask)); | 2020 __ testl(EAX, Immediate(kSmiTagMask)); |
| 2011 __ j(ZERO, deopt_blob->label()); | 2021 __ j(ZERO, deopt_blob->label()); |
| 2022 } else { |
| 2023 // Receiver cannot be Smi, no need to test it. |
| 2012 } | 2024 } |
| 2013 | 2025 |
| 2014 // We need to generate special test for last class exclusive Smi class. | 2026 intptr_t last_check_at = (smi_class_index == ic_data.NumberOfChecks() - 1) ? |
| 2015 intptr_t last_check_at = (smi_class_index == classes->length() - 1) ? | 2027 ic_data.NumberOfChecks() - 2 : ic_data.NumberOfChecks() - 1; |
| 2016 classes->length() - 2 : classes->length() - 1; | |
| 2017 // Every class may appear only once in the 'classes' array. Therefore, if | 2028 // Every class may appear only once in the 'classes' array. Therefore, if |
| 2018 // Smi class is last, it cannot be the second to last. | 2029 // Smi class is last, it cannot be the second to last. |
| 2019 ASSERT(!(*classes)[last_check_at]->IsSmi()); | |
| 2020 __ movl(EAX, FieldAddress(EAX, Object::class_offset())); // Receiver's class. | 2030 __ movl(EAX, FieldAddress(EAX, Object::class_offset())); // Receiver's class. |
| 2021 for (intptr_t i = 0; i <= last_check_at; i++) { | 2031 for (intptr_t i = 0; i <= last_check_at; i++) { |
| 2022 const Class& test_class = *((*classes)[i]); | 2032 Function& target = Function::ZoneHandle(); |
| 2023 ASSERT(!test_class.IsNullClass()); | 2033 Class& cls = Class::ZoneHandle(); |
| 2024 if (test_class.raw() == smi_class_.raw()) { | 2034 ic_data.GetOneClassCheckAt(i, &cls, &target); |
| 2035 ASSERT(!cls.IsNullClass()); |
| 2036 if (cls.raw() == smi_class_.raw()) { |
| 2037 ASSERT(i < last_check_at); // Smi class may not be last. |
| 2025 continue; // Skip Smi test. | 2038 continue; // Skip Smi test. |
| 2026 } | 2039 } |
| 2027 __ CompareObject(EAX, test_class); | 2040 __ CompareObject(EAX, cls); |
| 2028 if (i == last_check_at) { | 2041 if (i == last_check_at) { |
| 2029 __ j(NOT_EQUAL, deopt_blob->label()); | 2042 __ j(NOT_EQUAL, deopt_blob->label()); |
| 2030 GenerateDirectInstanceCall(node, test_class); | 2043 GenerateDirectCall(node_id, |
| 2044 token_index, |
| 2045 target, |
| 2046 num_args, |
| 2047 optional_argument_names); |
| 2031 } else { | 2048 } else { |
| 2032 Label next; | 2049 Label next; |
| 2033 __ j(NOT_EQUAL, &next); | 2050 __ j(NOT_EQUAL, &next); |
| 2034 GenerateDirectInstanceCall(node, test_class); | 2051 GenerateDirectCall(node_id, |
| 2052 token_index, |
| 2053 target, |
| 2054 num_args, |
| 2055 optional_argument_names); |
| 2035 __ jmp(&done); | 2056 __ jmp(&done); |
| 2036 __ Bind(&next); | 2057 __ Bind(&next); |
| 2037 } | 2058 } |
| 2038 } | 2059 } |
| 2039 __ Bind(&done); | 2060 __ Bind(&done); |
| 2040 | |
| 2041 return true; | |
| 2042 } | 2061 } |
| 2043 | 2062 |
| 2044 | 2063 |
| 2045 void OptimizingCodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) { | 2064 void OptimizingCodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) { |
| 2046 const int number_of_arguments = node->arguments()->length() + 1; | 2065 const int number_of_arguments = node->arguments()->length() + 1; |
| 2047 // Compute the receiver object and pass it as first argument to call. | 2066 // Compute the receiver object and pass it as first argument to call. |
| 2048 node->receiver()->Visit(this); | 2067 node->receiver()->Visit(this); |
| 2049 // Now compute rest of the arguments to the call. | 2068 // Now compute rest of the arguments to the call. |
| 2050 node->arguments()->Visit(this); | 2069 node->arguments()->Visit(this); |
| 2051 if (TryInlineInstanceCall(node)) { | 2070 if (TryInlineInstanceCall(node)) { |
| 2052 // Instance call is inlined. | 2071 // Instance call is inlined. |
| 2053 } else { | 2072 } else { |
| 2054 // Inline checks if possible and call function directly. | 2073 GenerateCheckedInstanceCalls(node, |
| 2055 if (!GenerateCheckedInstanceCalls(node)) { | 2074 node->receiver(), |
| 2056 GenerateInstanceCall(node->id(), | 2075 node->id(), |
| 2057 node->token_index(), | 2076 node->token_index(), |
| 2058 node->function_name(), | 2077 number_of_arguments, |
| 2059 number_of_arguments, | 2078 node->arguments()->names()); |
| 2060 node->arguments()->names()); | |
| 2061 } | |
| 2062 } | 2079 } |
| 2063 // Result is in EAX. | 2080 // Result is in EAX. |
| 2064 if (IsResultNeeded(node)) { | 2081 if (IsResultNeeded(node)) { |
| 2065 __ pushl(EAX); | 2082 __ pushl(EAX); |
| 2066 } | 2083 } |
| 2067 } | 2084 } |
| 2068 | 2085 |
| 2069 | 2086 |
| 2070 // Returns true if an instance call was replaced with its intrinsic. | 2087 // Returns true if an instance call was replaced with its intrinsic. |
| 2071 // Returns result in EAX. | 2088 // Returns result in EAX. |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2160 __ addl(ESP, Immediate(node->arguments()->length() * kWordSize)); | 2177 __ addl(ESP, Immediate(node->arguments()->length() * kWordSize)); |
| 2161 // Result is in EAX. | 2178 // Result is in EAX. |
| 2162 if (IsResultNeeded(node)) { | 2179 if (IsResultNeeded(node)) { |
| 2163 __ pushl(EAX); | 2180 __ pushl(EAX); |
| 2164 } | 2181 } |
| 2165 } | 2182 } |
| 2166 | 2183 |
| 2167 } // namespace dart | 2184 } // namespace dart |
| 2168 | 2185 |
| 2169 #endif // defined TARGET_ARCH_IA32 | 2186 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |