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

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

Issue 11262033: Simple array bounds check elimination on top of range analysis framework. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 1 month 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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/intermediate_language.h" 5 #include "vm/intermediate_language.h"
6 6
7 #include "vm/bit_vector.h" 7 #include "vm/bit_vector.h"
8 #include "vm/dart_entry.h" 8 #include "vm/dart_entry.h"
9 #include "vm/flow_graph_allocator.h" 9 #include "vm/flow_graph_allocator.h"
10 #include "vm/flow_graph_builder.h" 10 #include "vm/flow_graph_builder.h"
(...skipping 2059 matching lines...) Expand 10 before | Expand all | Expand 10 after
2070 for (Environment::DeepIterator it(copy); !it.Done(); it.Advance()) { 2070 for (Environment::DeepIterator it(copy); !it.Done(); it.Advance()) {
2071 Value* value = it.CurrentValue(); 2071 Value* value = it.CurrentValue();
2072 value->set_instruction(instr); 2072 value->set_instruction(instr);
2073 value->set_use_index(use_index++); 2073 value->set_use_index(use_index++);
2074 value->AddToEnvUseList(); 2074 value->AddToEnvUseList();
2075 } 2075 }
2076 instr->env()->outer_ = copy; 2076 instr->env()->outer_ = copy;
2077 } 2077 }
2078 2078
2079 2079
2080 RangeBoundary RangeBoundary::FromDefinition(Definition* defn, intptr_t offs) {
2081 if (defn->IsConstant() && defn->AsConstant()->value().IsSmi()) {
2082 return FromConstant(Smi::Cast(defn->AsConstant()->value()).Value());
2083 }
2084 return RangeBoundary(kSymbol, reinterpret_cast<intptr_t>(defn), offs);
2085 }
2086
2087
2080 RangeBoundary RangeBoundary::LowerBound() const { 2088 RangeBoundary RangeBoundary::LowerBound() const {
2081 if (IsConstant()) return *this; 2089 if (IsConstant()) return *this;
2082 if (symbol()->range() == NULL) return MinSmi(); 2090 if (symbol()->range() == NULL) return MinSmi();
2083 return Add(symbol()->range()->min().LowerBound(), 2091 return Add(symbol()->range()->min().LowerBound(),
2084 RangeBoundary::FromConstant(offset_), 2092 RangeBoundary::FromConstant(offset_),
2085 MinSmi()); 2093 MinSmi());
2086 } 2094 }
2087 2095
2088 2096
2089 RangeBoundary RangeBoundary::UpperBound() const { 2097 RangeBoundary RangeBoundary::UpperBound() const {
2090 if (IsConstant()) return *this; 2098 if (IsConstant()) return *this;
2091 if (symbol()->range() == NULL) return MaxSmi(); 2099 if (symbol()->range() == NULL) return MaxSmi();
2092 return Add(symbol()->range()->max().UpperBound(), 2100 return Add(symbol()->range()->max().UpperBound(),
2093 RangeBoundary::FromConstant(offset_), 2101 RangeBoundary::FromConstant(offset_),
2094 MaxSmi()); 2102 MaxSmi());
2095 } 2103 }
2096 2104
2097 2105
2106 static bool DependOnSameSymbol(const RangeBoundary& a, const RangeBoundary& b) {
2107 if (!a.IsSymbol() || !b.IsSymbol()) return false;
2108 if (a.symbol() == b.symbol()) return true;
2109
2110 return !a.symbol()->AffectedBySideEffect() &&
2111 !b.symbol()->AffectedBySideEffect() &&
2112 a.symbol()->Equals(b.symbol());
Florian Schneider 2012/10/26 11:10:07 I think !a->AffecteedBySideEffect() &&a->Equals(b)
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2113 }
2114
2115
2116 static bool IsMinSmi(Range* range) {
2117 return (range == NULL) ||
2118 (range->min().IsConstant() &&
2119 (range->min().value() <= Smi::kMinValue));
2120 }
2121
2122
2123 static bool IsMaxSmi(Range* range) {
2124 return (range == NULL) ||
2125 (range->max().IsConstant() &&
2126 (range->max().value() >= Smi::kMaxValue));
2127 }
2128
2129
2130 static bool IsEqual(const RangeBoundary& a, const RangeBoundary& b) {
2131 if (a.IsConstant() && b.IsConstant()) {
2132 return a.value() == b.value();
2133 } else if (a.IsSymbol() && b.IsSymbol()) {
2134 return (a.offset() == b.offset()) && DependOnSameSymbol(a, b);
2135 } else {
2136 return false;
2137 }
2138 }
2139
2140 static RangeBoundary CanonicalizeBoundary(const RangeBoundary& a) {
2141 if (a.IsConstant()) return a;
2142
2143 intptr_t offset = a.offset();
2144 Definition* symbol = a.symbol();
2145
2146 bool changed;
2147 do {
2148 changed = false;
2149 if (symbol->IsConstraint()) {
2150 symbol = symbol->AsConstraint()->value()->definition();
2151 changed = true;
2152 } else if (symbol->IsBinarySmiOp()) {
2153 BinarySmiOpInstr* op = symbol->AsBinarySmiOp();
2154 Definition* left = op->left()->definition();
2155 Definition* right = op->right()->definition();
2156 switch (op->op_kind()) {
2157 case Token::kADD:
2158 if (right->IsConstant()) {
2159 offset += Smi::Cast(right->AsConstant()->value()).Value();
2160 symbol = left;
2161 changed = true;
2162 } else if (left->IsConstant()) {
2163 offset += Smi::Cast(left->AsConstant()->value()).Value();
2164 symbol = right;
2165 changed = true;
2166 }
2167 break;
2168
2169
srdjan 2012/10/25 20:51:36 Remove one empty line
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2170 case Token::kSUB:
2171 if (right->IsConstant()) {
2172 offset -= Smi::Cast(right->AsConstant()->value()).Value();
2173 symbol = left;
2174 changed = true;
2175 }
2176 break;
2177
2178 default:
2179 break;
2180 }
2181 }
2182 } while (changed);
2183
2184 return RangeBoundary::FromDefinition(symbol, offset);
2185 }
2186
2187
2188 static bool CanonicalizeMaxBoundary(RangeBoundary* a) {
2189 if (!a->IsSymbol()) return false;
2190
2191 Range* range = a->symbol()->range();
2192 if ((range == NULL) || !range->max().IsSymbol()) return false;
2193
2194 *a = CanonicalizeBoundary(
2195 RangeBoundary::FromDefinition(range->max().symbol(),
2196 range->max().offset() + a->offset()));
2197
2198 return true;
2199 }
2200
2201
2202 static bool CanonicalizeMinBoundary(RangeBoundary* a) {
2203 if (!a->IsSymbol()) return false;
2204
2205 Range* range = a->symbol()->range();
2206 if ((range == NULL) || !range->min().IsSymbol()) return false;
2207
2208 *a = CanonicalizeBoundary(
2209 RangeBoundary::FromDefinition(range->min().symbol(),
2210 range->min().offset() + a->offset()));
2211
2212 return true;
2213 }
2214
2215
2216 #if 0
2217 static bool KnownToBeLessEqual(const RangeBoundary& a, const RangeBoundary& b) {
2218 RangeBoundary canonical_a = CanonicalizeBoundary(a);
2219 RangeBoundary canonical_b = CanonicalizeBoundary(b);
2220
2221 do {
2222 if (DependOnSameSymbol(canonical_a, canonical_b)) {
2223 return (canonical_a.offset() <= canonical_b.offset());
2224 }
2225 } while (CanonicalizeMaxBoundary(&canonical_a) ||
2226 CanonicalizeMinBoundary(&canonical_b));
2227
2228 return a.UpperBound().value() <= b.LowerBound().value();
2229 }
srdjan 2012/10/25 20:51:36 Remove the code above
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2230 #endif
2231
2232
2233 RangeBoundary RangeBoundary::Min(RangeBoundary a, RangeBoundary b) {
2234 if (DependOnSameSymbol(a, b)) {
2235 return (a.offset() <= b.offset()) ? a : b;
2236 }
2237
2238 const intptr_t min_a = a.LowerBound().value();
2239 const intptr_t min_b = b.LowerBound().value();
2240
2241 return RangeBoundary::FromConstant(Utils::Minimum(min_a, min_b));
2242 }
2243
2244
2245 RangeBoundary RangeBoundary::Max(RangeBoundary a, RangeBoundary b) {
2246 if (DependOnSameSymbol(a, b)) {
2247 return (a.offset() >= b.offset()) ? a : b;
2248 }
2249
2250 const intptr_t max_a = a.UpperBound().value();
2251 const intptr_t max_b = b.UpperBound().value();
2252
2253 return RangeBoundary::FromConstant(Utils::Maximum(max_a, max_b));
2254 }
2255
2256
2098 void Definition::InferRange() { 2257 void Definition::InferRange() {
2099 ASSERT(GetPropagatedCid() == kSmiCid); // Has meaning only for smis. 2258 ASSERT(GetPropagatedCid() == kSmiCid); // Has meaning only for smis.
2100 if (range_ == NULL) { 2259 if (range_ == NULL) {
2101 range_ = Range::Unknown(); 2260 range_ = Range::Unknown();
2102 } 2261 }
2103 } 2262 }
2104 2263
2105 2264
2106 void ConstantInstr::InferRange() { 2265 void ConstantInstr::InferRange() {
2107 ASSERT(value_.IsSmi()); 2266 ASSERT(value_.IsSmi());
2108 if (range_ == NULL) { 2267 if (range_ == NULL) {
2109 intptr_t value = Smi::Cast(value_).Value(); 2268 intptr_t value = Smi::Cast(value_).Value();
2110 range_ = new Range(RangeBoundary::FromConstant(value), 2269 range_ = new Range(RangeBoundary::FromConstant(value),
2111 RangeBoundary::FromConstant(value)); 2270 RangeBoundary::FromConstant(value));
2112 } 2271 }
2113 } 2272 }
2114 2273
2115 2274
2116 void ConstraintInstr::InferRange() { 2275 void ConstraintInstr::InferRange() {
2117 Range* value_range = value()->definition()->range(); 2276 Range* value_range = value()->definition()->range();
2118 2277
2119 // Compute intersection of constraint and value ranges. 2278 RangeBoundary min;
2120 range_ = new Range( 2279 RangeBoundary max;
2121 RangeBoundary::Max(Range::ConstantMin(value_range), 2280
2122 Range::ConstantMin(constraint())), 2281 if (IsMinSmi(value_range) && !IsMinSmi(constraint())) {
2123 RangeBoundary::Min(Range::ConstantMax(value_range), 2282 min = constraint()->min();
2124 Range::ConstantMax(constraint()))); 2283 } else if (IsMinSmi(constraint()) && !IsMinSmi(value_range)) {
2284 min = value_range->min();
2285 } else if ((value_range != NULL) &&
2286 IsEqual(constraint()->min(), value_range->min())) {
2287 min = constraint()->min();
2288 } else {
2289 if (value_range != NULL) {
2290 RangeBoundary canonical_a = CanonicalizeBoundary(constraint()->min());
2291 RangeBoundary canonical_b = CanonicalizeBoundary(value_range->min());
2292
2293 do {
2294 if (DependOnSameSymbol(canonical_a, canonical_b)) {
2295 min = (canonical_a.offset() <= canonical_b.offset()) ? canonical_b
2296 : canonical_a;
2297 }
2298 } while (CanonicalizeMinBoundary(&canonical_a) ||
2299 CanonicalizeMinBoundary(&canonical_b));
2300 }
2301
2302 if (min.IsUnknown()) {
2303 min = RangeBoundary::Max(Range::ConstantMin(value_range),
2304 Range::ConstantMin(constraint()));
2305 }
2306 }
2307
2308 if (IsMaxSmi(value_range) && !IsMaxSmi(constraint())) {
2309 max = constraint()->max();
2310 } else if (IsMaxSmi(constraint()) && !IsMaxSmi(value_range)) {
2311 max = value_range->max();
2312 } else if ((value_range != NULL) &&
2313 IsEqual(constraint()->max(), value_range->max())) {
2314 max = constraint()->max();
2315 } else {
2316 if (value_range != NULL) {
2317 RangeBoundary canonical_b = CanonicalizeBoundary(value_range->max());
2318 RangeBoundary canonical_a = CanonicalizeBoundary(constraint()->max());
2319
2320 do {
2321 if (DependOnSameSymbol(canonical_a, canonical_b)) {
2322 max = (canonical_a.offset() <= canonical_b.offset()) ? canonical_a
2323 : canonical_b;
2324 break;
2325 }
2326 } while (CanonicalizeMaxBoundary(&canonical_a) ||
2327 CanonicalizeMaxBoundary(&canonical_b));
2328 }
2329
2330 if (max.IsUnknown()) {
2331 max = RangeBoundary::Min(Range::ConstantMax(value_range),
2332 Range::ConstantMax(constraint()));
Florian Schneider 2012/10/26 11:10:07 Indentation.
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2333 }
2334 }
2335
2336 range_ = new Range(min, max);
2125 } 2337 }
2126 2338
2127 2339
2340 void LoadFieldInstr::InferRange() {
2341 if ((range_ == NULL) &&
2342 ((recognized_kind() == MethodRecognizer::kObjectArrayLength) ||
2343 (recognized_kind() == MethodRecognizer::kImmutableArrayLength))) {
2344 range_ = new Range(RangeBoundary::FromConstant(0),
2345 RangeBoundary::FromConstant(Array::kMaxElements));
2346 return;
2347 }
2348 Definition::InferRange();
2349 }
2350
2351
2128 void PhiInstr::InferRange() { 2352 void PhiInstr::InferRange() {
2129 RangeBoundary new_min; 2353 RangeBoundary new_min;
2130 RangeBoundary new_max; 2354 RangeBoundary new_max;
2131 2355
2132 for (intptr_t i = 0; i < InputCount(); i++) { 2356 for (intptr_t i = 0; i < InputCount(); i++) {
2133 Range* input_range = InputAt(i)->definition()->range(); 2357 Range* input_range = InputAt(i)->definition()->range();
2134 if (input_range == NULL) { 2358 if (input_range == NULL) {
2135 range_ = Range::Unknown(); 2359 range_ = Range::Unknown();
2136 return; 2360 return;
2137 } 2361 }
(...skipping 14 matching lines...) Expand all
2152 ASSERT(new_min.IsUnknown() == new_max.IsUnknown()); 2376 ASSERT(new_min.IsUnknown() == new_max.IsUnknown());
2153 if (new_min.IsUnknown()) { 2377 if (new_min.IsUnknown()) {
2154 range_ = Range::Unknown(); 2378 range_ = Range::Unknown();
2155 return; 2379 return;
2156 } 2380 }
2157 2381
2158 range_ = new Range(new_min, new_max); 2382 range_ = new Range(new_min, new_max);
2159 } 2383 }
2160 2384
2161 2385
2386 static bool SymbolicSub(const RangeBoundary& a,
2387 const RangeBoundary& b,
2388 RangeBoundary* result) {
2389 if (a.IsSymbol() &&
2390 b.IsConstant() &&
2391 ((b.value() > Smi::kMinValue) && (b.value() < Smi::kMaxValue))) {
2392 *result = RangeBoundary::FromDefinition(a.symbol(), a.offset() - b.value());
Florian Schneider 2012/10/26 11:10:07 Guard against smi-overflow here.
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2393 return true;
2394 }
2395 return false;
2396 }
2397
2398
2399 static bool SymbolicAdd(const RangeBoundary& a,
2400 const RangeBoundary& b,
2401 RangeBoundary* result) {
2402 if (a.IsSymbol() &&
2403 b.IsConstant() &&
2404 ((b.value() > Smi::kMinValue) && (b.value() < Smi::kMaxValue))) {
2405 *result = RangeBoundary::FromDefinition(a.symbol(), a.offset() + b.value());
Florian Schneider 2012/10/26 11:10:07 Guard against overflow here, so that the result of
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2406 return true;
2407 } else if (b.IsSymbol() &&
2408 a.IsConstant() &&
2409 ((a.value() > Smi::kMinValue) && (a.value() < Smi::kMaxValue))) {
2410 *result = RangeBoundary::FromDefinition(b.symbol(), b.offset() + a.value());
Florian Schneider 2012/10/26 11:10:07 Guard against smi-overflow here as well.
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2411 return true;
2412 }
2413 return false;
2414 }
2415
2416
2162 void BinarySmiOpInstr::InferRange() { 2417 void BinarySmiOpInstr::InferRange() {
2163 Range* left_range = left()->definition()->range(); 2418 Definition* left_defn = left()->definition();
2419
2420 Range* left_range = left_defn->range();
2164 Range* right_range = right()->definition()->range(); 2421 Range* right_range = right()->definition()->range();
2165 2422
2166 if ((left_range == NULL) || (right_range == NULL)) { 2423 if ((left_range == NULL) || (right_range == NULL)) {
2167 range_ = new Range(RangeBoundary::MinSmi(), RangeBoundary::MaxSmi()); 2424 range_ = new Range(RangeBoundary::MinSmi(), RangeBoundary::MaxSmi());
2168 return; 2425 return;
2169 } 2426 }
2170 2427
2171 RangeBoundary new_min; 2428 RangeBoundary left_min =
2172 RangeBoundary new_max; 2429 left_defn->IsLoadField() ?
Florian Schneider 2012/10/26 11:10:07 Does this need to be for every LoadField, or only
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Narrowed symbolic computation to length.
2430 RangeBoundary::FromDefinition(left_defn) : left_range->min();
2431
2432 RangeBoundary left_max =
2433 left_defn->IsLoadField() ?
2434 RangeBoundary::FromDefinition(left_defn) : left_range->max();
Florian Schneider 2012/10/26 11:10:07 Maybe add a TODO to support the case where right()
Vyacheslav Egorov (Google) 2012/10/26 14:23:29 Done.
2435
2436 RangeBoundary min;
2437 RangeBoundary max;
2173 switch (op_kind()) { 2438 switch (op_kind()) {
2174 case Token::kADD: 2439 case Token::kADD:
2175 new_min = 2440 if (!SymbolicAdd(left_min, right_range->min(), &min)) {
2176 RangeBoundary::Add(Range::ConstantMin(left_range), 2441 min =
2177 Range::ConstantMin(right_range), 2442 RangeBoundary::Add(Range::ConstantMin(left_range),
2178 RangeBoundary::OverflowedMinSmi()); 2443 Range::ConstantMin(right_range),
2179 new_max = 2444 RangeBoundary::OverflowedMinSmi());
2180 RangeBoundary::Add(Range::ConstantMax(left_range), 2445 }
2181 Range::ConstantMax(right_range), 2446
2182 RangeBoundary::OverflowedMaxSmi()); 2447 if (!SymbolicAdd(left_max, right_range->max(), &max)) {
2448 max =
2449 RangeBoundary::Add(Range::ConstantMax(right_range),
2450 Range::ConstantMax(left_range),
2451 RangeBoundary::OverflowedMaxSmi());
2452 }
2183 break; 2453 break;
2184 2454
2185 case Token::kSUB: 2455 case Token::kSUB:
2186 new_min = 2456 if (!SymbolicSub(left_min, right_range->max(), &min)) {
2187 RangeBoundary::Sub(Range::ConstantMin(left_range), 2457 min =
2188 Range::ConstantMax(right_range), 2458 RangeBoundary::Sub(Range::ConstantMin(left_range),
2189 RangeBoundary::OverflowedMinSmi()); 2459 Range::ConstantMax(right_range),
2190 new_max = 2460 RangeBoundary::OverflowedMinSmi());
2191 RangeBoundary::Sub(Range::ConstantMax(left_range), 2461 }
2192 Range::ConstantMin(right_range), 2462
2193 RangeBoundary::OverflowedMaxSmi()); 2463 if (!SymbolicSub(left_max, right_range->min(), &max)) {
2464 max =
2465 RangeBoundary::Sub(Range::ConstantMax(left_range),
2466 Range::ConstantMin(right_range),
2467 RangeBoundary::OverflowedMaxSmi());
2468 }
2194 break; 2469 break;
2195 2470
2196 default: 2471 default:
2197 if (range_ == NULL) { 2472 if (range_ == NULL) {
2198 range_ = Range::Unknown(); 2473 range_ = Range::Unknown();
2199 } 2474 }
2200 return; 2475 return;
2201 } 2476 }
2202 2477
2203 ASSERT(!new_min.IsUnknown() && !new_max.IsUnknown()); 2478 ASSERT(!min.IsUnknown() && !max.IsUnknown());
2204 set_overflow(new_min.Overflowed() || new_max.Overflowed()); 2479 set_overflow(min.LowerBound().Overflowed() || max.UpperBound().Overflowed());
2205 2480
2206 range_ = new Range(new_min.Clamp(), new_max.Clamp()); 2481 if (min.IsConstant()) min.Clamp();
2482 if (max.IsConstant()) max.Clamp();
2483
2484 range_ = new Range(min, max);
2207 } 2485 }
2208 2486
2209 2487
2488 static bool IsArrayLength(Definition* defn, Definition* array) {
2489 LoadFieldInstr* load = defn->AsLoadField();
2490 return (load != NULL) && (load->value()->definition() == array) &&
2491 ((load->recognized_kind() == MethodRecognizer::kObjectArrayLength) ||
2492 (load->recognized_kind() == MethodRecognizer::kImmutableArrayLength));
2493 }
2494
2495
2496 bool CheckArrayBoundInstr::IsRedundant() {
2497 // Check that array has an immutable length.
2498 if ((array_type() != kArrayCid) && (array_type() != kImmutableArrayCid)) {
2499 return false;
2500 }
2501
2502 Range* index_range = index()->definition()->range();
2503
2504 // Range of the index is unknown can't decide if the check is redundant.
2505 if (index_range == NULL) return false;
2506
2507 // Range of the index is not positive. Check can't be redundant.
2508 if (Range::ConstantMin(index_range).value() < 0) return false;
2509
2510 RangeBoundary max = CanonicalizeBoundary(index_range->max());
2511 do {
2512 if (max.IsSymbol() &&
2513 (max.offset() < 0) &&
2514 IsArrayLength(max.symbol(), array()->definition())) {
2515 return true;
2516 }
2517 } while (CanonicalizeMaxBoundary(&max));
2518
2519 // Failed to prove that maximum is bounded with array length.
2520 return false;
2521 }
2522
2523
2210 #undef __ 2524 #undef __
2211 2525
2212 } // namespace dart 2526 } // namespace dart
OLDNEW
« runtime/vm/intermediate_language.h ('K') | « runtime/vm/intermediate_language.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698