Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(225)

Side by Side Diff: runtime/vm/aot_optimizer.cc

Issue 2074533002: Simplify and improve optimization of is-tests in the precompiler. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/aot_optimizer.h" 5 #include "vm/aot_optimizer.h"
6 6
7 #include "vm/bit_vector.h" 7 #include "vm/bit_vector.h"
8 #include "vm/branch_optimizer.h" 8 #include "vm/branch_optimizer.h"
9 #include "vm/cha.h" 9 #include "vm/cha.h"
10 #include "vm/compiler.h" 10 #include "vm/compiler.h"
(...skipping 1931 matching lines...) Expand 10 before | Expand all | Expand 10 after
1942 InstanceCallInstr* call, 1942 InstanceCallInstr* call,
1943 MethodRecognizer::Kind recognized_kind) { 1943 MethodRecognizer::Kind recognized_kind) {
1944 // Cannot handle unboxed instructions. 1944 // Cannot handle unboxed instructions.
1945 return false; 1945 return false;
1946 } 1946 }
1947 1947
1948 1948
1949 // If type tests specified by 'ic_data' do not depend on type arguments, 1949 // If type tests specified by 'ic_data' do not depend on type arguments,
1950 // return mapping cid->result in 'results' (i : cid; i + 1: result). 1950 // return mapping cid->result in 'results' (i : cid; i + 1: result).
1951 // If all tests yield the same result, return it otherwise return Bool::null. 1951 // If all tests yield the same result, return it otherwise return Bool::null.
1952 // If no mapping is possible, 'results' is empty. 1952 // If no mapping is possible, 'results' has less than
1953 // (ic_data.NumberOfChecks() * 2) entries
1953 // An instance-of test returning all same results can be converted to a class 1954 // An instance-of test returning all same results can be converted to a class
1954 // check. 1955 // check.
1955 RawBool* AotOptimizer::InstanceOfAsBool( 1956 RawBool* AotOptimizer::InstanceOfAsBool(
1956 const ICData& ic_data, 1957 const ICData& ic_data,
1957 const AbstractType& type, 1958 const AbstractType& type,
1958 ZoneGrowableArray<intptr_t>* results) const { 1959 ZoneGrowableArray<intptr_t>* results) const {
1959 ASSERT(results->is_empty()); 1960 ASSERT(results->is_empty());
1960 ASSERT(ic_data.NumArgsTested() == 1); // Unary checks only. 1961 ASSERT(ic_data.NumArgsTested() == 1); // Unary checks only.
1961 if (type.IsFunctionType() || type.IsDartFunctionType() || 1962 if (type.IsFunctionType() || type.IsDartFunctionType() ||
1962 !type.IsInstantiated() || type.IsMalformedOrMalbounded()) { 1963 !type.IsInstantiated() || type.IsMalformedOrMalbounded()) {
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
2071 results->Add(result); 2072 results->Add(result);
2072 } 2073 }
2073 } 2074 }
2074 2075
2075 2076
2076 // Tries to add cid tests to 'results' so that no deoptimization is 2077 // Tries to add cid tests to 'results' so that no deoptimization is
2077 // necessary. 2078 // necessary.
2078 // TODO(srdjan): Do also for other than 'int' type. 2079 // TODO(srdjan): Do also for other than 'int' type.
2079 static bool TryExpandTestCidsResult(ZoneGrowableArray<intptr_t>* results, 2080 static bool TryExpandTestCidsResult(ZoneGrowableArray<intptr_t>* results,
2080 const AbstractType& type) { 2081 const AbstractType& type) {
2081 ASSERT(results->length() >= 2); // At least on eentry. 2082 ASSERT(results->length() >= 2); // At least on entry.
2082 const ClassTable& class_table = *Isolate::Current()->class_table(); 2083 const ClassTable& class_table = *Isolate::Current()->class_table();
2083 if ((*results)[0] != kSmiCid) { 2084 if ((*results)[0] != kSmiCid) {
2084 const Class& cls = Class::Handle(class_table.At(kSmiCid)); 2085 const Class& cls = Class::Handle(class_table.At(kSmiCid));
2085 const Class& type_class = Class::Handle(type.type_class()); 2086 const Class& type_class = Class::Handle(type.type_class());
2086 const bool smi_is_subtype = cls.IsSubtypeOf(TypeArguments::Handle(), 2087 const bool smi_is_subtype = cls.IsSubtypeOf(TypeArguments::Handle(),
2087 type_class, 2088 type_class,
2088 TypeArguments::Handle(), 2089 TypeArguments::Handle(),
2089 NULL, 2090 NULL,
2090 NULL, 2091 NULL,
2091 Heap::kOld); 2092 Heap::kOld);
2092 results->Add((*results)[results->length() - 2]); 2093 results->Add((*results)[results->length() - 2]);
2093 results->Add((*results)[results->length() - 2]); 2094 results->Add((*results)[results->length() - 2]);
2094 for (intptr_t i = results->length() - 3; i > 1; --i) { 2095 for (intptr_t i = results->length() - 3; i > 1; --i) {
2095 (*results)[i] = (*results)[i - 2]; 2096 (*results)[i] = (*results)[i - 2];
2096 } 2097 }
2097 (*results)[0] = kSmiCid; 2098 (*results)[0] = kSmiCid;
2098 (*results)[1] = smi_is_subtype; 2099 (*results)[1] = smi_is_subtype;
2099 } 2100 }
2100 2101
2101 ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded()); 2102 ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded());
2102 ASSERT(results->length() >= 2); 2103 ASSERT(results->length() >= 2);
2103 if (type.IsIntType()) { 2104 if (type.IsSmiType()) {
2105 ASSERT((*results)[0] == kSmiCid);
2106 return false;
2107 } else if (type.IsIntType()) {
2104 ASSERT((*results)[0] == kSmiCid); 2108 ASSERT((*results)[0] == kSmiCid);
2105 TryAddTest(results, kMintCid, true); 2109 TryAddTest(results, kMintCid, true);
2106 TryAddTest(results, kBigintCid, true); 2110 TryAddTest(results, kBigintCid, true);
2107 // Cannot deoptimize since all tests returning true have been added. 2111 // Cannot deoptimize since all tests returning true have been added.
2108 return false; 2112 return false;
2113 } else if (type.IsNumberType()) {
2114 ASSERT((*results)[0] == kSmiCid);
2115 TryAddTest(results, kMintCid, true);
2116 TryAddTest(results, kBigintCid, true);
2117 TryAddTest(results, kDoubleCid, true);
2118 return false;
2119 } else if (type.IsDoubleType()) {
2120 ASSERT((*results)[0] == kSmiCid);
2121 TryAddTest(results, kDoubleCid, true);
2122 return false;
2109 } 2123 }
2110
2111 return true; // May deoptimize since we have not identified all 'true' tests. 2124 return true; // May deoptimize since we have not identified all 'true' tests.
2112 } 2125 }
2113 2126
2114 2127
2115 // TODO(srdjan): Use ICData to check if always true or false. 2128 // TODO(srdjan): Use ICData to check if always true or false.
2116 void AotOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) { 2129 void AotOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) {
2117 ASSERT(Token::IsTypeTestOperator(call->token_kind())); 2130 ASSERT(Token::IsTypeTestOperator(call->token_kind()));
2118 // Guard against repeated speculative inlining.
2119 if (!use_speculative_inlining_ ||
2120 IsBlackListedForInlining(call->deopt_id())) {
2121 return;
2122 }
2123 Definition* left = call->ArgumentAt(0); 2131 Definition* left = call->ArgumentAt(0);
2124 Definition* type_args = NULL; 2132 Definition* type_args = NULL;
2125 AbstractType& type = AbstractType::ZoneHandle(Z); 2133 AbstractType& type = AbstractType::ZoneHandle(Z);
2126 bool negate = false; 2134 bool negate = false;
2127 if (call->ArgumentCount() == 2) { 2135 if (call->ArgumentCount() == 2) {
2128 type_args = flow_graph()->constant_null(); 2136 type_args = flow_graph()->constant_null();
2129 if (call->function_name().raw() == 2137 if (call->function_name().raw() ==
2130 Library::PrivateCoreLibName(Symbols::_instanceOfNum()).raw()) { 2138 Library::PrivateCoreLibName(Symbols::_instanceOfNum()).raw()) {
2131 type = Type::Number(); 2139 type = Type::Number();
2132 } else if (call->function_name().raw() == 2140 } else if (call->function_name().raw() ==
(...skipping 12 matching lines...) Expand all
2145 UNIMPLEMENTED(); 2153 UNIMPLEMENTED();
2146 } 2154 }
2147 negate = Bool::Cast(call->ArgumentAt(1)->OriginalDefinition() 2155 negate = Bool::Cast(call->ArgumentAt(1)->OriginalDefinition()
2148 ->AsConstant()->value()).value(); 2156 ->AsConstant()->value()).value();
2149 } else { 2157 } else {
2150 type_args = call->ArgumentAt(1); 2158 type_args = call->ArgumentAt(1);
2151 type = AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()).raw(); 2159 type = AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()).raw();
2152 negate = Bool::Cast(call->ArgumentAt(3)->OriginalDefinition() 2160 negate = Bool::Cast(call->ArgumentAt(3)->OriginalDefinition()
2153 ->AsConstant()->value()).value(); 2161 ->AsConstant()->value()).value();
2154 } 2162 }
2155 const ICData& unary_checks =
2156 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks());
2157 if ((unary_checks.NumberOfChecks() > 0) &&
2158 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) {
2159 ZoneGrowableArray<intptr_t>* results =
2160 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2);
2161 Bool& as_bool =
2162 Bool::ZoneHandle(Z, InstanceOfAsBool(unary_checks, type, results));
2163 if (as_bool.IsNull()) {
2164 if (results->length() == unary_checks.NumberOfChecks() * 2) {
2165 const bool can_deopt = TryExpandTestCidsResult(results, type);
2166 TestCidsInstr* test_cids = new(Z) TestCidsInstr(
2167 call->token_pos(),
2168 negate ? Token::kISNOT : Token::kIS,
2169 new(Z) Value(left),
2170 *results,
2171 can_deopt ? call->deopt_id() : Thread::kNoDeoptId);
2172 // Remove type.
2173 ReplaceCall(call, test_cids);
2174 return;
2175 }
2176 } else {
2177 // TODO(srdjan): Use TestCidsInstr also for this case.
2178 // One result only.
2179 AddReceiverCheck(call);
2180 if (negate) {
2181 as_bool = Bool::Get(!as_bool.value()).raw();
2182 }
2183 ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool);
2184 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
2185 PushArgumentInstr* push = call->PushArgumentAt(i);
2186 push->ReplaceUsesWith(push->value()->definition());
2187 push->RemoveFromGraph();
2188 }
2189 call->ReplaceUsesWith(bool_const);
2190 ASSERT(current_iterator()->Current() == call);
2191 current_iterator()->RemoveCurrentFromGraph();
2192 return;
2193 }
2194 }
2195 2163
2196 if (TypeCheckAsClassEquality(type)) { 2164 if (TypeCheckAsClassEquality(type)) {
2197 LoadClassIdInstr* left_cid = new(Z) LoadClassIdInstr(new(Z) Value(left)); 2165 LoadClassIdInstr* left_cid = new(Z) LoadClassIdInstr(new(Z) Value(left));
2198 InsertBefore(call, 2166 InsertBefore(call,
2199 left_cid, 2167 left_cid,
2200 NULL, 2168 NULL,
2201 FlowGraph::kValue); 2169 FlowGraph::kValue);
2202 const intptr_t type_cid = Class::Handle(Z, type.type_class()).id(); 2170 const intptr_t type_cid = Class::Handle(Z, type.type_class()).id();
2203 ConstantInstr* cid = 2171 ConstantInstr* cid =
2204 flow_graph()->GetConstant(Smi::Handle(Z, Smi::New(type_cid))); 2172 flow_graph()->GetConstant(Smi::Handle(Z, Smi::New(type_cid)));
2205 2173
2206 StrictCompareInstr* check_cid = 2174 StrictCompareInstr* check_cid =
2207 new(Z) StrictCompareInstr( 2175 new(Z) StrictCompareInstr(
2208 call->token_pos(), 2176 call->token_pos(),
2209 negate ? Token::kNE_STRICT : Token::kEQ_STRICT, 2177 negate ? Token::kNE_STRICT : Token::kEQ_STRICT,
2210 new(Z) Value(left_cid), 2178 new(Z) Value(left_cid),
2211 new(Z) Value(cid), 2179 new(Z) Value(cid),
2212 false); // No number check. 2180 false); // No number check.
2213 ReplaceCall(call, check_cid); 2181 ReplaceCall(call, check_cid);
2214 return; 2182 return;
2215 } 2183 }
2216 2184
2185 const ICData& unary_checks =
2186 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks());
2187 if ((unary_checks.NumberOfChecks() > 0) &&
2188 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) {
2189 ZoneGrowableArray<intptr_t>* results =
2190 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2);
2191 InstanceOfAsBool(unary_checks, type, results);
2192 if (results->length() == unary_checks.NumberOfChecks() * 2) {
2193 const bool can_deopt = TryExpandTestCidsResult(results, type);
2194 if (can_deopt && !IsAllowedForInlining(call->deopt_id())) {
2195 // Guard against repeated speculative inlining.
2196 return;
2197 }
2198 TestCidsInstr* test_cids = new(Z) TestCidsInstr(
2199 call->token_pos(),
2200 negate ? Token::kISNOT : Token::kIS,
2201 new(Z) Value(left),
2202 *results,
2203 can_deopt ? call->deopt_id() : Thread::kNoDeoptId);
2204 // Remove type.
2205 ReplaceCall(call, test_cids);
2206 return;
2207 }
2208 }
2209
2217 InstanceOfInstr* instance_of = 2210 InstanceOfInstr* instance_of =
2218 new(Z) InstanceOfInstr(call->token_pos(), 2211 new(Z) InstanceOfInstr(call->token_pos(),
2219 new(Z) Value(left), 2212 new(Z) Value(left),
2220 new(Z) Value(type_args), 2213 new(Z) Value(type_args),
2221 type, 2214 type,
2222 negate, 2215 negate,
2223 call->deopt_id()); 2216 call->deopt_id());
2224 ReplaceCall(call, instance_of); 2217 ReplaceCall(call, instance_of);
2225 } 2218 }
2226 2219
2227 2220
2228 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids). 2221 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids).
2229 void AotOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) { 2222 void AotOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) {
2230 ASSERT(Token::IsTypeCastOperator(call->token_kind())); 2223 ASSERT(Token::IsTypeCastOperator(call->token_kind()));
2231 // Guard against repeated speculative inlining.
2232 if (!use_speculative_inlining_ ||
2233 IsBlackListedForInlining(call->deopt_id())) {
2234 return;
2235 }
2236 Definition* left = call->ArgumentAt(0); 2224 Definition* left = call->ArgumentAt(0);
2237 Definition* type_args = call->ArgumentAt(1); 2225 Definition* type_args = call->ArgumentAt(1);
2238 const AbstractType& type = 2226 const AbstractType& type =
2239 AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()); 2227 AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value());
2240 ASSERT(!type.IsMalformedOrMalbounded()); 2228 ASSERT(!type.IsMalformedOrMalbounded());
2241 const ICData& unary_checks = 2229 const ICData& unary_checks =
2242 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); 2230 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks());
2243 if ((unary_checks.NumberOfChecks() > 0) && 2231 if ((unary_checks.NumberOfChecks() > 0) &&
2244 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { 2232 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) {
2245 ZoneGrowableArray<intptr_t>* results = 2233 ZoneGrowableArray<intptr_t>* results =
2246 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2); 2234 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2);
2247 const Bool& as_bool = Bool::ZoneHandle(Z, 2235 const Bool& as_bool = Bool::ZoneHandle(Z,
2248 InstanceOfAsBool(unary_checks, type, results)); 2236 InstanceOfAsBool(unary_checks, type, results));
2249 if (as_bool.raw() == Bool::True().raw()) { 2237 if (as_bool.raw() == Bool::True().raw()) {
2238 // Guard against repeated speculative inlining.
2239 if (!IsAllowedForInlining(call->deopt_id())) {
2240 return;
2241 }
2250 AddReceiverCheck(call); 2242 AddReceiverCheck(call);
2251 // Remove the original push arguments. 2243 // Remove the original push arguments.
2252 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { 2244 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
2253 PushArgumentInstr* push = call->PushArgumentAt(i); 2245 PushArgumentInstr* push = call->PushArgumentAt(i);
2254 push->ReplaceUsesWith(push->value()->definition()); 2246 push->ReplaceUsesWith(push->value()->definition());
2255 push->RemoveFromGraph(); 2247 push->RemoveFromGraph();
2256 } 2248 }
2257 // Remove call, replace it with 'left'. 2249 // Remove call, replace it with 'left'.
2258 call->ReplaceUsesWith(left); 2250 call->ReplaceUsesWith(left);
2259 ASSERT(current_iterator()->Current() == call); 2251 ASSERT(current_iterator()->Current() == call);
2260 current_iterator()->RemoveCurrentFromGraph(); 2252 current_iterator()->RemoveCurrentFromGraph();
2261 return; 2253 return;
2262 } 2254 }
2263 } 2255 }
2264 AssertAssignableInstr* assert_as = 2256 AssertAssignableInstr* assert_as =
2265 new(Z) AssertAssignableInstr(call->token_pos(), 2257 new(Z) AssertAssignableInstr(call->token_pos(),
2266 new(Z) Value(left), 2258 new(Z) Value(left),
2267 new(Z) Value(type_args), 2259 new(Z) Value(type_args),
2268 type, 2260 type,
2269 Symbols::InTypeCast(), 2261 Symbols::InTypeCast(),
2270 call->deopt_id()); 2262 call->deopt_id());
2271 ReplaceCall(call, assert_as); 2263 ReplaceCall(call, assert_as);
2272 } 2264 }
2273 2265
2274 2266
2275 bool AotOptimizer::IsBlackListedForInlining(intptr_t call_deopt_id) { 2267 bool AotOptimizer::IsAllowedForInlining(intptr_t call_deopt_id) {
2268 if (!use_speculative_inlining_) return false;
2276 for (intptr_t i = 0; i < inlining_black_list_->length(); ++i) { 2269 for (intptr_t i = 0; i < inlining_black_list_->length(); ++i) {
2277 if ((*inlining_black_list_)[i] == call_deopt_id) return true; 2270 if ((*inlining_black_list_)[i] == call_deopt_id) return false;
2278 } 2271 }
2279 return false; 2272 return true;
2280 } 2273 }
2281 2274
2282 2275
2283 static bool HasLikelySmiOperand(InstanceCallInstr* instr) { 2276 static bool HasLikelySmiOperand(InstanceCallInstr* instr) {
2284 // Phis with at least one known smi are // guessed to be likely smi as well. 2277 // Phis with at least one known smi are // guessed to be likely smi as well.
2285 for (intptr_t i = 0; i < instr->ArgumentCount(); ++i) { 2278 for (intptr_t i = 0; i < instr->ArgumentCount(); ++i) {
2286 PhiInstr* phi = instr->ArgumentAt(i)->AsPhi(); 2279 PhiInstr* phi = instr->ArgumentAt(i)->AsPhi();
2287 if (phi != NULL) { 2280 if (phi != NULL) {
2288 for (intptr_t j = 0; j < phi->InputCount(); ++j) { 2281 for (intptr_t j = 0; j < phi->InputCount(); ++j) {
2289 if (phi->InputAt(j)->Type()->ToCid() == kSmiCid) return true; 2282 if (phi->InputAt(j)->Type()->ToCid() == kSmiCid) return true;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
2322 return; 2315 return;
2323 } 2316 }
2324 const ICData& unary_checks = 2317 const ICData& unary_checks =
2325 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); 2318 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks());
2326 if ((unary_checks.NumberOfChecks() > 0) && 2319 if ((unary_checks.NumberOfChecks() > 0) &&
2327 (op_kind == Token::kSET) && 2320 (op_kind == Token::kSET) &&
2328 TryInlineInstanceSetter(instr, unary_checks)) { 2321 TryInlineInstanceSetter(instr, unary_checks)) {
2329 return; 2322 return;
2330 } 2323 }
2331 2324
2332 if (use_speculative_inlining_ && 2325 if (IsAllowedForInlining(instr->deopt_id()) &&
2333 !IsBlackListedForInlining(instr->deopt_id()) &&
2334 (unary_checks.NumberOfChecks() > 0)) { 2326 (unary_checks.NumberOfChecks() > 0)) {
2335 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { 2327 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) {
2336 return; 2328 return;
2337 } 2329 }
2338 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { 2330 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) {
2339 return; 2331 return;
2340 } 2332 }
2341 if ((op_kind == Token::kEQ) && TryReplaceWithEqualityOp(instr, op_kind)) { 2333 if ((op_kind == Token::kEQ) && TryReplaceWithEqualityOp(instr, op_kind)) {
2342 return; 2334 return;
2343 } 2335 }
(...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after
2800 2792
2801 // Discard the environment from the original instruction because the store 2793 // Discard the environment from the original instruction because the store
2802 // can't deoptimize. 2794 // can't deoptimize.
2803 instr->RemoveEnvironment(); 2795 instr->RemoveEnvironment();
2804 ReplaceCall(instr, store); 2796 ReplaceCall(instr, store);
2805 return true; 2797 return true;
2806 } 2798 }
2807 2799
2808 2800
2809 } // namespace dart 2801 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698