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 intptr_t FlowGraphCompiler::ComputeGoodBiasForCidComparison( |
1821 const GrowableArray<CidRangeTarget>& sorted, | 1814 const CallTargets& sorted, |
1822 intptr_t max_immediate) { | 1815 intptr_t max_immediate) { |
1823 // Sometimes a bias can be useful so we can emit more compact compare | 1816 // Sometimes a bias can be useful so we can emit more compact compare |
1824 // instructions. | 1817 // instructions. |
1825 intptr_t min_cid = 1000000; | 1818 intptr_t min_cid = 1000000; |
1826 intptr_t max_cid = -1; | 1819 intptr_t max_cid = -1; |
1827 | 1820 |
1828 const intptr_t sorted_len = sorted.length(); | 1821 const intptr_t sorted_len = sorted.length(); |
1829 | 1822 |
1830 for (intptr_t i = 0; i < sorted_len + 1; i++) { | 1823 for (intptr_t i = 0; i < sorted_len + 1; i++) { |
1831 bool done = (i == sorted_len); | 1824 bool done = (i == sorted_len); |
(...skipping 10 matching lines...) Expand all Loading... | |
1842 } | 1835 } |
1843 } | 1836 } |
1844 min_cid = Utils::Minimum(min_cid, start); | 1837 min_cid = Utils::Minimum(min_cid, start); |
1845 max_cid = Utils::Maximum(max_cid, end); | 1838 max_cid = Utils::Maximum(max_cid, end); |
1846 } | 1839 } |
1847 UNREACHABLE(); | 1840 UNREACHABLE(); |
1848 return 0; | 1841 return 0; |
1849 } | 1842 } |
1850 | 1843 |
1851 | 1844 |
1845 bool FlowGraphCompiler::LookupMethodFor(int class_id, | |
1846 const String& name, | |
1847 const ArgumentsDescriptor& args_desc, | |
1848 Function* fn_return) { | |
1849 Thread* thread = Thread::Current(); | |
1850 Isolate* isolate = thread->isolate(); | |
1851 Zone* zone = thread->zone(); | |
1852 if (class_id < 0) return false; | |
1853 if (class_id >= isolate->class_table()->NumCids()) return false; | |
1854 | |
1855 RawClass* raw_class = isolate->class_table()->At(class_id); | |
1856 if (raw_class == NULL) return false; | |
1857 Class& cls = Class::Handle(zone, raw_class); | |
1858 if (cls.IsNull()) return false; | |
1859 if (!cls.is_finalized()) return false; | |
1860 if (Array::Handle(cls.functions()).IsNull()) return false; | |
1861 | |
1862 const bool allow_add = false; | |
1863 Function& target_function = | |
1864 Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass( | |
1865 cls, name, args_desc, allow_add)); | |
1866 if (target_function.IsNull()) return false; | |
1867 *fn_return ^= target_function.raw(); | |
1868 return true; | |
1869 } | |
1870 | |
1871 | |
1852 #if !defined(TARGET_ARCH_DBC) | 1872 #if !defined(TARGET_ARCH_DBC) |
1853 // DBC emits calls very differently from other architectures due to its | 1873 // DBC emits calls very differently from other architectures due to its |
1854 // interpreted nature. | 1874 // interpreted nature. |
1855 void FlowGraphCompiler::EmitPolymorphicInstanceCall(const ICData& ic_data, | 1875 void FlowGraphCompiler::EmitPolymorphicInstanceCall( |
1856 intptr_t argument_count, | 1876 const CallTargets& targets, |
1857 const Array& argument_names, | 1877 const InstanceCallInstr& original_call, |
1858 intptr_t deopt_id, | 1878 intptr_t argument_count, |
1859 TokenPosition token_pos, | 1879 const Array& argument_names, |
1860 LocationSummary* locs, | 1880 intptr_t deopt_id, |
1861 bool complete, | 1881 TokenPosition token_pos, |
1862 intptr_t total_ic_calls) { | 1882 LocationSummary* locs, |
1883 bool complete, | |
1884 intptr_t total_ic_calls) { | |
1863 if (FLAG_polymorphic_with_deopt) { | 1885 if (FLAG_polymorphic_with_deopt) { |
1864 Label* deopt = | 1886 Label* deopt = |
1865 AddDeoptStub(deopt_id, ICData::kDeoptPolymorphicInstanceCallTestFail); | 1887 AddDeoptStub(deopt_id, ICData::kDeoptPolymorphicInstanceCallTestFail); |
1866 Label ok; | 1888 Label ok; |
1867 EmitTestAndCall(ic_data, argument_count, argument_names, | 1889 EmitTestAndCall(targets, original_call.function_name(), argument_count, |
1890 argument_names, | |
1868 deopt, // No cid match. | 1891 deopt, // No cid match. |
1869 &ok, // Found cid. | 1892 &ok, // Found cid. |
1870 deopt_id, token_pos, locs, complete, total_ic_calls); | 1893 deopt_id, token_pos, locs, complete, total_ic_calls); |
1871 assembler()->Bind(&ok); | 1894 assembler()->Bind(&ok); |
1872 } else { | 1895 } else { |
1873 if (complete) { | 1896 if (complete) { |
1874 Label ok; | 1897 Label ok; |
1875 EmitTestAndCall(ic_data, argument_count, argument_names, | 1898 EmitTestAndCall(targets, original_call.function_name(), argument_count, |
1899 argument_names, | |
1876 NULL, // No cid match. | 1900 NULL, // No cid match. |
1877 &ok, // Found cid. | 1901 &ok, // Found cid. |
1878 deopt_id, token_pos, locs, true, total_ic_calls); | 1902 deopt_id, token_pos, locs, true, total_ic_calls); |
1879 assembler()->Bind(&ok); | 1903 assembler()->Bind(&ok); |
1880 } else { | 1904 } else { |
1881 EmitSwitchableInstanceCall(ic_data, argument_count, deopt_id, token_pos, | 1905 const ICData& unary_checks = ICData::ZoneHandle( |
1882 locs); | 1906 zone(), original_call.ic_data()->AsUnaryClassChecks()); |
1907 EmitSwitchableInstanceCall(unary_checks, argument_count, deopt_id, | |
1908 token_pos, locs); | |
1883 } | 1909 } |
1884 } | 1910 } |
1885 } | 1911 } |
1912 | |
1913 | |
1914 #define __ assembler()-> | |
1915 void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets, | |
1916 const String& function_name, | |
1917 intptr_t argument_count, | |
1918 const Array& argument_names, | |
1919 Label* failed, | |
1920 Label* match_found, | |
1921 intptr_t deopt_id, | |
1922 TokenPosition token_index, | |
1923 LocationSummary* locs, | |
1924 bool complete, | |
1925 intptr_t total_ic_calls) { | |
1926 ASSERT(is_optimizing()); | |
1927 | |
1928 const Array& arguments_descriptor = Array::ZoneHandle( | |
1929 zone(), ArgumentsDescriptor::New(argument_count, argument_names)); | |
1930 | |
1931 EmitTestAndCallLoadReceiver(argument_count, arguments_descriptor); | |
1932 | |
1933 static const int kNoCase = -1; | |
1934 int smi_case = kNoCase; | |
1935 int which_case_to_skip = kNoCase; | |
1936 | |
1937 const int length = targets.length(); | |
1938 ASSERT(length > 0); | |
1939 int non_smi_length = length; | |
1940 | |
1941 // Find out if one of the classes in one of the cases is the Smi class. We | |
1942 // will be handling that specially. | |
1943 for (int i = 0; i < length; i++) { | |
1944 const intptr_t start = targets[i].cid_start; | |
1945 if (start > kSmiCid) continue; | |
1946 const intptr_t end = targets[i].cid_end; | |
1947 if (end >= kSmiCid) { | |
1948 smi_case = i; | |
1949 if (start == kSmiCid && end == kSmiCid) { | |
1950 // If this case has only the Smi class then we won't need to emit it at | |
1951 // all later. | |
1952 which_case_to_skip = i; | |
1953 non_smi_length--; | |
1954 } | |
1955 break; | |
1956 } | |
1957 } | |
1958 | |
1959 if (smi_case != kNoCase) { | |
1960 Label after_smi_test; | |
1961 // Jump if receiver is not Smi. | |
1962 EmitTestAndCallSmiBranch(non_smi_length == 0 ? failed : &after_smi_test, | |
1963 false); | |
1964 | |
1965 // Do not use the code from the function, but let the code be patched so | |
1966 // that we can record the outgoing edges to other code. | |
1967 const Function& function = *targets[smi_case].target; | |
1968 GenerateStaticDartCall(deopt_id, token_index, | |
1969 *StubCode::CallStaticFunction_entry(), | |
1970 RawPcDescriptors::kOther, locs, function); | |
1971 __ Drop(argument_count); | |
1972 if (match_found != NULL) { | |
1973 EmitTestAndCallGotoMatchFound(match_found); | |
1974 } | |
1975 __ Bind(&after_smi_test); | |
1976 } else { | |
1977 if (!complete) { | |
1978 // Smi is not a valid class so jump to fail if it's a Smi. | |
1979 EmitTestAndCallSmiBranch(failed, true); | |
Vyacheslav Egorov (Google)
2017/04/20 16:45:33
please add a comment what true/false means in this
erikcorry
2017/04/21 13:04:43
Done.
| |
1980 } | |
1981 } | |
1982 | |
1983 if (non_smi_length == 0) { | |
1984 // If non_smi_length is 0 then only a Smi check was needed; the Smi check | |
1985 // above will fail if there was only one check and receiver is not Smi. | |
1986 return; | |
1987 } | |
1988 | |
1989 bool add_megamorphic_call = false; | |
1990 const int kMaxImmediateInInstruction = 256; | |
Vyacheslav Egorov (Google)
2017/04/20 16:45:33
Maybe add a note that this works on all architectu
erikcorry
2017/04/21 13:04:43
This wasn't a very noticable win (microscopic code
| |
1991 int bias = | |
1992 ComputeGoodBiasForCidComparison(targets, kMaxImmediateInInstruction); | |
1993 | |
1994 // Value is not Smi. | |
1995 EmitTestAndCallLoadCid(bias); | |
1996 | |
1997 int last_check = which_case_to_skip == length - 1 ? length - 2 : length - 1; | |
1998 | |
1999 for (intptr_t i = 0; i < length; i++) { | |
2000 if (i == which_case_to_skip) continue; | |
2001 const bool is_last_check = (i == last_check); | |
2002 const int count = targets[i].count; | |
2003 if (!is_last_check && !complete && count < (total_ic_calls >> 5)) { | |
2004 // This case is hit too rarely to be worth writing class-id checks inline | |
2005 // for. Note that we can't do this for calls with only one target because | |
2006 // the type propagator may have made use of that and expects a deopt if | |
2007 // a new class is seen at this calls site. See IsMonomorphic. | |
2008 add_megamorphic_call = true; | |
2009 break; | |
2010 } | |
2011 Label next_test; | |
2012 if (!complete || !is_last_check) { | |
2013 bias = EmitTestAndCallCheckCid(is_last_check ? failed : &next_test, | |
2014 targets[i], bias); | |
2015 } | |
2016 // Do not use the code from the function, but let the code be patched so | |
2017 // that we can record the outgoing edges to other code. | |
2018 const Function& function = *targets[i].target; | |
2019 GenerateStaticDartCall(deopt_id, token_index, | |
2020 *StubCode::CallStaticFunction_entry(), | |
2021 RawPcDescriptors::kOther, locs, function); | |
2022 __ Drop(argument_count); | |
2023 if (!is_last_check || add_megamorphic_call) { | |
2024 EmitTestAndCallGotoMatchFound(match_found); | |
2025 } | |
2026 __ Bind(&next_test); | |
2027 } | |
2028 if (add_megamorphic_call) { | |
2029 int try_index = CatchClauseNode::kInvalidTryIndex; | |
2030 EmitMegamorphicInstanceCall(function_name, arguments_descriptor, | |
2031 argument_count, deopt_id, token_index, locs, | |
2032 try_index); | |
2033 } | |
2034 } | |
2035 #undef __ | |
1886 #endif | 2036 #endif |
1887 | 2037 |
1888 #if defined(DEBUG) && !defined(TARGET_ARCH_DBC) | 2038 #if defined(DEBUG) && !defined(TARGET_ARCH_DBC) |
1889 // TODO(vegorov) re-enable frame state tracking on DBC. It is | 2039 // TODO(vegorov) re-enable frame state tracking on DBC. It is |
1890 // currently disabled because it relies on LocationSummaries and | 2040 // currently disabled because it relies on LocationSummaries and |
1891 // we don't use them during unoptimized compilation on DBC. | 2041 // we don't use them during unoptimized compilation on DBC. |
1892 void FlowGraphCompiler::FrameStateUpdateWith(Instruction* instr) { | 2042 void FlowGraphCompiler::FrameStateUpdateWith(Instruction* instr) { |
1893 ASSERT(!is_optimizing()); | 2043 ASSERT(!is_optimizing()); |
1894 | 2044 |
1895 switch (instr->tag()) { | 2045 switch (instr->tag()) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1954 | 2104 |
1955 | 2105 |
1956 void FlowGraphCompiler::FrameStateClear() { | 2106 void FlowGraphCompiler::FrameStateClear() { |
1957 ASSERT(!is_optimizing()); | 2107 ASSERT(!is_optimizing()); |
1958 frame_state_.TruncateTo(0); | 2108 frame_state_.TruncateTo(0); |
1959 } | 2109 } |
1960 #endif // defined(DEBUG) && !defined(TARGET_ARCH_DBC) | 2110 #endif // defined(DEBUG) && !defined(TARGET_ARCH_DBC) |
1961 | 2111 |
1962 | 2112 |
1963 } // namespace dart | 2113 } // namespace dart |
OLD | NEW |