OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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_XXX. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_XXX. |
6 | 6 |
7 #include "vm/flow_graph_compiler.h" | 7 #include "vm/flow_graph_compiler.h" |
8 | 8 |
9 #include "vm/bit_vector.h" | 9 #include "vm/bit_vector.h" |
10 #include "vm/cha.h" | 10 #include "vm/cha.h" |
11 #include "vm/compiler.h" | 11 #include "vm/compiler.h" |
12 #include "vm/dart_entry.h" | 12 #include "vm/dart_entry.h" |
13 #include "vm/debugger.h" | 13 #include "vm/debugger.h" |
14 #include "vm/deopt_instructions.h" | 14 #include "vm/deopt_instructions.h" |
15 #include "vm/exceptions.h" | 15 #include "vm/exceptions.h" |
16 #include "vm/flags.h" | 16 #include "vm/flags.h" |
17 #include "vm/flow_graph_allocator.h" | 17 #include "vm/flow_graph_allocator.h" |
18 #include "vm/il_printer.h" | 18 #include "vm/il_printer.h" |
19 #include "vm/intrinsifier.h" | 19 #include "vm/intrinsifier.h" |
20 #include "vm/locations.h" | 20 #include "vm/locations.h" |
21 #include "vm/log.h" | 21 #include "vm/log.h" |
22 #include "vm/longjump.h" | 22 #include "vm/longjump.h" |
23 #include "vm/object_store.h" | 23 #include "vm/object_store.h" |
24 #include "vm/parser.h" | 24 #include "vm/parser.h" |
25 #include "vm/raw_object.h" | 25 #include "vm/raw_object.h" |
| 26 #include "vm/resolver.h" |
26 #include "vm/stack_frame.h" | 27 #include "vm/stack_frame.h" |
27 #include "vm/stub_code.h" | 28 #include "vm/stub_code.h" |
28 #include "vm/symbols.h" | 29 #include "vm/symbols.h" |
29 #include "vm/timeline.h" | 30 #include "vm/timeline.h" |
30 | 31 |
31 namespace dart { | 32 namespace dart { |
32 | 33 |
33 DEFINE_FLAG(bool, | 34 DEFINE_FLAG(bool, |
34 enable_simd_inline, | 35 enable_simd_inline, |
35 true, | 36 true, |
(...skipping 1126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1162 *StubCode::TwoArgsOptimizedCheckInlineCache_entry(), ic_data, | 1163 *StubCode::TwoArgsOptimizedCheckInlineCache_entry(), ic_data, |
1163 argument_count, deopt_id, token_pos, locs); | 1164 argument_count, deopt_id, token_pos, locs); |
1164 return; | 1165 return; |
1165 default: | 1166 default: |
1166 UNIMPLEMENTED(); | 1167 UNIMPLEMENTED(); |
1167 } | 1168 } |
1168 return; | 1169 return; |
1169 } | 1170 } |
1170 | 1171 |
1171 if (is_optimizing()) { | 1172 if (is_optimizing()) { |
1172 EmitMegamorphicInstanceCall(ic_data_in, argument_count, deopt_id, token_pos, | 1173 String& name = String::Handle(ic_data_in.target_name()); |
1173 locs, CatchClauseNode::kInvalidTryIndex); | 1174 Array& arguments_descriptor = |
| 1175 Array::Handle(ic_data_in.arguments_descriptor()); |
| 1176 EmitMegamorphicInstanceCall(name, arguments_descriptor, argument_count, |
| 1177 deopt_id, token_pos, locs, |
| 1178 CatchClauseNode::kInvalidTryIndex); |
1174 return; | 1179 return; |
1175 } | 1180 } |
1176 | 1181 |
1177 switch (ic_data.NumArgsTested()) { | 1182 switch (ic_data.NumArgsTested()) { |
1178 case 1: | 1183 case 1: |
1179 EmitInstanceCall(*StubCode::OneArgCheckInlineCache_entry(), ic_data, | 1184 EmitInstanceCall(*StubCode::OneArgCheckInlineCache_entry(), ic_data, |
1180 argument_count, deopt_id, token_pos, locs); | 1185 argument_count, deopt_id, token_pos, locs); |
1181 break; | 1186 break; |
1182 case 2: | 1187 case 2: |
1183 EmitInstanceCall(*StubCode::TwoArgsCheckInlineCache_entry(), ic_data, | 1188 EmitInstanceCall(*StubCode::TwoArgsCheckInlineCache_entry(), ic_data, |
(...skipping 596 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1780 code_source_map_builder_->BeginCodeSourceRange(assembler()->CodeSize()); | 1785 code_source_map_builder_->BeginCodeSourceRange(assembler()->CodeSize()); |
1781 } | 1786 } |
1782 | 1787 |
1783 | 1788 |
1784 void FlowGraphCompiler::EndCodeSourceRange(TokenPosition token_pos) { | 1789 void FlowGraphCompiler::EndCodeSourceRange(TokenPosition token_pos) { |
1785 code_source_map_builder_->EndCodeSourceRange(assembler()->CodeSize(), | 1790 code_source_map_builder_->EndCodeSourceRange(assembler()->CodeSize(), |
1786 token_pos); | 1791 token_pos); |
1787 } | 1792 } |
1788 | 1793 |
1789 | 1794 |
1790 const ICData& FlowGraphCompiler::TrySpecializeICDataByReceiverCid( | 1795 const CallTargets* FlowGraphCompiler::ResolveCallTargetsForReceiverCid( |
1791 const ICData& ic_data, | 1796 intptr_t cid, |
1792 intptr_t cid) { | 1797 const String& selector, |
| 1798 const Array& args_desc_array) { |
1793 Zone* zone = Thread::Current()->zone(); | 1799 Zone* zone = Thread::Current()->zone(); |
1794 if (ic_data.NumArgsTested() != 1) return ic_data; | |
1795 | 1800 |
1796 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { | 1801 ArgumentsDescriptor args_desc(args_desc_array); |
1797 return ic_data; // Nothing to do | |
1798 } | |
1799 | 1802 |
1800 intptr_t count = 1; | 1803 Function& fn = Function::ZoneHandle(zone); |
1801 const Function& function = | 1804 if (!LookupMethodFor(cid, selector, args_desc, &fn)) return NULL; |
1802 Function::Handle(zone, ic_data.GetTargetForReceiverClassId(cid, &count)); | |
1803 // TODO(fschneider): Try looking up the function on the class if it is | |
1804 // not found in the ICData. | |
1805 if (!function.IsNull()) { | |
1806 const ICData& new_ic_data = ICData::ZoneHandle( | |
1807 zone, ICData::New(Function::Handle(zone, ic_data.Owner()), | |
1808 String::Handle(zone, ic_data.target_name()), | |
1809 Object::empty_array(), // Dummy argument descriptor. | |
1810 ic_data.deopt_id(), ic_data.NumArgsTested(), false)); | |
1811 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); | |
1812 new_ic_data.AddReceiverCheck(cid, function, count); | |
1813 return new_ic_data; | |
1814 } | |
1815 | 1805 |
1816 return ic_data; | 1806 CallTargets* targets = new (zone) CallTargets(); |
| 1807 targets->Add(CidRangeTarget(cid, cid, &fn, /* count = */ 1)); |
| 1808 |
| 1809 return targets; |
1817 } | 1810 } |
1818 | 1811 |
1819 | 1812 |
1820 intptr_t FlowGraphCompiler::ComputeGoodBiasForCidComparison( | 1813 bool FlowGraphCompiler::LookupMethodFor(int class_id, |
1821 const GrowableArray<CidRangeTarget>& sorted, | 1814 const String& name, |
1822 intptr_t max_immediate) { | 1815 const ArgumentsDescriptor& args_desc, |
1823 // Sometimes a bias can be useful so we can emit more compact compare | 1816 Function* fn_return) { |
1824 // instructions. | 1817 Thread* thread = Thread::Current(); |
1825 intptr_t min_cid = 1000000; | 1818 Isolate* isolate = thread->isolate(); |
1826 intptr_t max_cid = -1; | 1819 Zone* zone = thread->zone(); |
| 1820 if (class_id < 0) return false; |
| 1821 if (class_id >= isolate->class_table()->NumCids()) return false; |
1827 | 1822 |
1828 const intptr_t sorted_len = sorted.length(); | 1823 RawClass* raw_class = isolate->class_table()->At(class_id); |
| 1824 if (raw_class == NULL) return false; |
| 1825 Class& cls = Class::Handle(zone, raw_class); |
| 1826 if (cls.IsNull()) return false; |
| 1827 if (!cls.is_finalized()) return false; |
| 1828 if (Array::Handle(cls.functions()).IsNull()) return false; |
1829 | 1829 |
1830 for (intptr_t i = 0; i < sorted_len + 1; i++) { | 1830 const bool allow_add = false; |
1831 bool done = (i == sorted_len); | 1831 Function& target_function = |
1832 intptr_t start = done ? 0 : sorted[i].cid_start; | 1832 Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass( |
1833 intptr_t end = done ? 0 : sorted[i].cid_end; | 1833 cls, name, args_desc, allow_add)); |
1834 bool is_range = start != end; | 1834 if (target_function.IsNull()) return false; |
1835 bool spread_too_big = start - min_cid > max_immediate; | 1835 *fn_return ^= target_function.raw(); |
1836 if (done || is_range || spread_too_big) { | 1836 return true; |
1837 if (i >= 2 && max_cid - min_cid <= max_immediate && | |
1838 max_cid > max_immediate) { | |
1839 return min_cid; | |
1840 } else { | |
1841 return 0; | |
1842 } | |
1843 } | |
1844 min_cid = Utils::Minimum(min_cid, start); | |
1845 max_cid = Utils::Maximum(max_cid, end); | |
1846 } | |
1847 UNREACHABLE(); | |
1848 return 0; | |
1849 } | 1837 } |
1850 | 1838 |
1851 | 1839 |
1852 #if !defined(TARGET_ARCH_DBC) | 1840 #if !defined(TARGET_ARCH_DBC) |
1853 // DBC emits calls very differently from other architectures due to its | 1841 // DBC emits calls very differently from other architectures due to its |
1854 // interpreted nature. | 1842 // interpreted nature. |
1855 void FlowGraphCompiler::EmitPolymorphicInstanceCall(const ICData& ic_data, | 1843 void FlowGraphCompiler::EmitPolymorphicInstanceCall( |
1856 intptr_t argument_count, | 1844 const CallTargets& targets, |
1857 const Array& argument_names, | 1845 const InstanceCallInstr& original_call, |
1858 intptr_t deopt_id, | 1846 intptr_t argument_count, |
1859 TokenPosition token_pos, | 1847 const Array& argument_names, |
1860 LocationSummary* locs, | 1848 intptr_t deopt_id, |
1861 bool complete, | 1849 TokenPosition token_pos, |
1862 intptr_t total_ic_calls) { | 1850 LocationSummary* locs, |
| 1851 bool complete, |
| 1852 intptr_t total_ic_calls) { |
1863 if (FLAG_polymorphic_with_deopt) { | 1853 if (FLAG_polymorphic_with_deopt) { |
1864 Label* deopt = | 1854 Label* deopt = |
1865 AddDeoptStub(deopt_id, ICData::kDeoptPolymorphicInstanceCallTestFail); | 1855 AddDeoptStub(deopt_id, ICData::kDeoptPolymorphicInstanceCallTestFail); |
1866 Label ok; | 1856 Label ok; |
1867 EmitTestAndCall(ic_data, argument_count, argument_names, | 1857 EmitTestAndCall(targets, original_call.function_name(), argument_count, |
| 1858 argument_names, |
1868 deopt, // No cid match. | 1859 deopt, // No cid match. |
1869 &ok, // Found cid. | 1860 &ok, // Found cid. |
1870 deopt_id, token_pos, locs, complete, total_ic_calls); | 1861 deopt_id, token_pos, locs, complete, total_ic_calls); |
1871 assembler()->Bind(&ok); | 1862 assembler()->Bind(&ok); |
1872 } else { | 1863 } else { |
1873 if (complete) { | 1864 if (complete) { |
1874 Label ok; | 1865 Label ok; |
1875 EmitTestAndCall(ic_data, argument_count, argument_names, | 1866 EmitTestAndCall(targets, original_call.function_name(), argument_count, |
| 1867 argument_names, |
1876 NULL, // No cid match. | 1868 NULL, // No cid match. |
1877 &ok, // Found cid. | 1869 &ok, // Found cid. |
1878 deopt_id, token_pos, locs, true, total_ic_calls); | 1870 deopt_id, token_pos, locs, true, total_ic_calls); |
1879 assembler()->Bind(&ok); | 1871 assembler()->Bind(&ok); |
1880 } else { | 1872 } else { |
1881 EmitSwitchableInstanceCall(ic_data, argument_count, deopt_id, token_pos, | 1873 const ICData& unary_checks = ICData::ZoneHandle( |
1882 locs); | 1874 zone(), original_call.ic_data()->AsUnaryClassChecks()); |
| 1875 EmitSwitchableInstanceCall(unary_checks, argument_count, deopt_id, |
| 1876 token_pos, locs); |
1883 } | 1877 } |
1884 } | 1878 } |
1885 } | 1879 } |
| 1880 |
| 1881 |
| 1882 #define __ assembler()-> |
| 1883 void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets, |
| 1884 const String& function_name, |
| 1885 intptr_t argument_count, |
| 1886 const Array& argument_names, |
| 1887 Label* failed, |
| 1888 Label* match_found, |
| 1889 intptr_t deopt_id, |
| 1890 TokenPosition token_index, |
| 1891 LocationSummary* locs, |
| 1892 bool complete, |
| 1893 intptr_t total_ic_calls) { |
| 1894 ASSERT(is_optimizing()); |
| 1895 |
| 1896 const Array& arguments_descriptor = Array::ZoneHandle( |
| 1897 zone(), ArgumentsDescriptor::New(argument_count, argument_names)); |
| 1898 |
| 1899 EmitTestAndCallLoadReceiver(argument_count, arguments_descriptor); |
| 1900 |
| 1901 static const int kNoCase = -1; |
| 1902 int smi_case = kNoCase; |
| 1903 int which_case_to_skip = kNoCase; |
| 1904 |
| 1905 const int length = targets.length(); |
| 1906 ASSERT(length > 0); |
| 1907 int non_smi_length = length; |
| 1908 |
| 1909 // Find out if one of the classes in one of the cases is the Smi class. We |
| 1910 // will be handling that specially. |
| 1911 for (int i = 0; i < length; i++) { |
| 1912 const intptr_t start = targets[i].cid_start; |
| 1913 if (start > kSmiCid) continue; |
| 1914 const intptr_t end = targets[i].cid_end; |
| 1915 if (end >= kSmiCid) { |
| 1916 smi_case = i; |
| 1917 if (start == kSmiCid && end == kSmiCid) { |
| 1918 // If this case has only the Smi class then we won't need to emit it at |
| 1919 // all later. |
| 1920 which_case_to_skip = i; |
| 1921 non_smi_length--; |
| 1922 } |
| 1923 break; |
| 1924 } |
| 1925 } |
| 1926 |
| 1927 if (smi_case != kNoCase) { |
| 1928 Label after_smi_test; |
| 1929 EmitTestAndCallSmiBranch(non_smi_length == 0 ? failed : &after_smi_test, |
| 1930 /* jump_if_smi= */ false); |
| 1931 |
| 1932 // Do not use the code from the function, but let the code be patched so |
| 1933 // that we can record the outgoing edges to other code. |
| 1934 const Function& function = *targets[smi_case].target; |
| 1935 GenerateStaticDartCall(deopt_id, token_index, |
| 1936 *StubCode::CallStaticFunction_entry(), |
| 1937 RawPcDescriptors::kOther, locs, function); |
| 1938 __ Drop(argument_count); |
| 1939 if (match_found != NULL) { |
| 1940 __ Jump(match_found); |
| 1941 } |
| 1942 __ Bind(&after_smi_test); |
| 1943 } else { |
| 1944 if (!complete) { |
| 1945 // Smi is not a valid class. |
| 1946 EmitTestAndCallSmiBranch(failed, /* jump_if_smi = */ true); |
| 1947 } |
| 1948 } |
| 1949 |
| 1950 if (non_smi_length == 0) { |
| 1951 // If non_smi_length is 0 then only a Smi check was needed; the Smi check |
| 1952 // above will fail if there was only one check and receiver is not Smi. |
| 1953 return; |
| 1954 } |
| 1955 |
| 1956 bool add_megamorphic_call = false; |
| 1957 int bias = 0; |
| 1958 |
| 1959 // Value is not Smi. |
| 1960 EmitTestAndCallLoadCid(); |
| 1961 |
| 1962 int last_check = which_case_to_skip == length - 1 ? length - 2 : length - 1; |
| 1963 |
| 1964 for (intptr_t i = 0; i < length; i++) { |
| 1965 if (i == which_case_to_skip) continue; |
| 1966 const bool is_last_check = (i == last_check); |
| 1967 const int count = targets[i].count; |
| 1968 if (!is_last_check && !complete && count < (total_ic_calls >> 5)) { |
| 1969 // This case is hit too rarely to be worth writing class-id checks inline |
| 1970 // for. Note that we can't do this for calls with only one target because |
| 1971 // the type propagator may have made use of that and expects a deopt if |
| 1972 // a new class is seen at this calls site. See IsMonomorphic. |
| 1973 add_megamorphic_call = true; |
| 1974 break; |
| 1975 } |
| 1976 Label next_test; |
| 1977 if (!complete || !is_last_check) { |
| 1978 bias = EmitTestAndCallCheckCid(is_last_check ? failed : &next_test, |
| 1979 targets[i], bias); |
| 1980 } |
| 1981 // Do not use the code from the function, but let the code be patched so |
| 1982 // that we can record the outgoing edges to other code. |
| 1983 const Function& function = *targets[i].target; |
| 1984 GenerateStaticDartCall(deopt_id, token_index, |
| 1985 *StubCode::CallStaticFunction_entry(), |
| 1986 RawPcDescriptors::kOther, locs, function); |
| 1987 __ Drop(argument_count); |
| 1988 if (!is_last_check || add_megamorphic_call) { |
| 1989 __ Jump(match_found); |
| 1990 } |
| 1991 __ Bind(&next_test); |
| 1992 } |
| 1993 if (add_megamorphic_call) { |
| 1994 int try_index = CatchClauseNode::kInvalidTryIndex; |
| 1995 EmitMegamorphicInstanceCall(function_name, arguments_descriptor, |
| 1996 argument_count, deopt_id, token_index, locs, |
| 1997 try_index); |
| 1998 } |
| 1999 } |
| 2000 #undef __ |
1886 #endif | 2001 #endif |
1887 | 2002 |
1888 #if defined(DEBUG) && !defined(TARGET_ARCH_DBC) | 2003 #if defined(DEBUG) && !defined(TARGET_ARCH_DBC) |
1889 // TODO(vegorov) re-enable frame state tracking on DBC. It is | 2004 // TODO(vegorov) re-enable frame state tracking on DBC. It is |
1890 // currently disabled because it relies on LocationSummaries and | 2005 // currently disabled because it relies on LocationSummaries and |
1891 // we don't use them during unoptimized compilation on DBC. | 2006 // we don't use them during unoptimized compilation on DBC. |
1892 void FlowGraphCompiler::FrameStateUpdateWith(Instruction* instr) { | 2007 void FlowGraphCompiler::FrameStateUpdateWith(Instruction* instr) { |
1893 ASSERT(!is_optimizing()); | 2008 ASSERT(!is_optimizing()); |
1894 | 2009 |
1895 switch (instr->tag()) { | 2010 switch (instr->tag()) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1954 | 2069 |
1955 | 2070 |
1956 void FlowGraphCompiler::FrameStateClear() { | 2071 void FlowGraphCompiler::FrameStateClear() { |
1957 ASSERT(!is_optimizing()); | 2072 ASSERT(!is_optimizing()); |
1958 frame_state_.TruncateTo(0); | 2073 frame_state_.TruncateTo(0); |
1959 } | 2074 } |
1960 #endif // defined(DEBUG) && !defined(TARGET_ARCH_DBC) | 2075 #endif // defined(DEBUG) && !defined(TARGET_ARCH_DBC) |
1961 | 2076 |
1962 | 2077 |
1963 } // namespace dart | 2078 } // namespace dart |
OLD | NEW |