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

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: address comments 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
« no previous file with comments | « runtime/vm/intermediate_language.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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() + offs);
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 // Returns true if two range boundaries refer to the same symbol.
2107 static bool DependOnSameSymbol(const RangeBoundary& a, const RangeBoundary& b) {
2108 if (!a.IsSymbol() || !b.IsSymbol()) return false;
2109 if (a.symbol() == b.symbol()) return true;
2110
2111 return !a.symbol()->AffectedBySideEffect() &&
2112 a.symbol()->Equals(b.symbol());
2113 }
2114
2115
2116 // Returns true if range has a least specific minimum value.
2117 static bool IsMinSmi(Range* range) {
2118 return (range == NULL) ||
2119 (range->min().IsConstant() &&
2120 (range->min().value() <= Smi::kMinValue));
2121 }
2122
2123
2124 // Returns true if range has a least specific maximium value.
2125 static bool IsMaxSmi(Range* range) {
2126 return (range == NULL) ||
2127 (range->max().IsConstant() &&
2128 (range->max().value() >= Smi::kMaxValue));
2129 }
2130
2131
2132 // Returns true if two range boundaries can be proven to be equal.
2133 static bool IsEqual(const RangeBoundary& a, const RangeBoundary& b) {
2134 if (a.IsConstant() && b.IsConstant()) {
2135 return a.value() == b.value();
2136 } else if (a.IsSymbol() && b.IsSymbol()) {
2137 return (a.offset() == b.offset()) && DependOnSameSymbol(a, b);
2138 } else {
2139 return false;
2140 }
2141 }
2142
2143
2144 static RangeBoundary CanonicalizeBoundary(const RangeBoundary& a,
2145 const RangeBoundary& overflow) {
2146 if (a.IsConstant()) return a;
2147
2148 intptr_t offset = a.offset();
2149 Definition* symbol = a.symbol();
2150
2151 bool changed;
2152 do {
2153 changed = false;
2154 if (symbol->IsConstraint()) {
2155 symbol = symbol->AsConstraint()->value()->definition();
2156 changed = true;
2157 } else if (symbol->IsBinarySmiOp()) {
2158 BinarySmiOpInstr* op = symbol->AsBinarySmiOp();
2159 Definition* left = op->left()->definition();
2160 Definition* right = op->right()->definition();
2161 switch (op->op_kind()) {
2162 case Token::kADD:
2163 if (right->IsConstant()) {
2164 offset += Smi::Cast(right->AsConstant()->value()).Value();
2165 symbol = left;
2166 changed = true;
2167 } else if (left->IsConstant()) {
2168 offset += Smi::Cast(left->AsConstant()->value()).Value();
2169 symbol = right;
2170 changed = true;
2171 }
2172 break;
2173
2174 case Token::kSUB:
2175 if (right->IsConstant()) {
2176 offset -= Smi::Cast(right->AsConstant()->value()).Value();
2177 symbol = left;
2178 changed = true;
2179 }
2180 break;
2181
2182 default:
2183 break;
2184 }
2185 }
2186
2187 if (!Smi::IsValid(offset)) return overflow;
2188 } while (changed);
2189
2190 return RangeBoundary::FromDefinition(symbol, offset);
2191 }
2192
2193
2194 static bool CanonicalizeMaxBoundary(RangeBoundary* a) {
2195 if (!a->IsSymbol()) return false;
2196
2197 Range* range = a->symbol()->range();
2198 if ((range == NULL) || !range->max().IsSymbol()) return false;
2199
2200 const intptr_t offset = range->max().offset() + a->offset();
2201
2202 if (!Smi::IsValid(offset)) {
2203 *a = RangeBoundary::OverflowedMaxSmi();
2204 return true;
2205 }
2206
2207 *a = CanonicalizeBoundary(
2208 RangeBoundary::FromDefinition(range->max().symbol(), offset),
2209 RangeBoundary::OverflowedMaxSmi());
2210
2211 return true;
2212 }
2213
2214
2215 static bool CanonicalizeMinBoundary(RangeBoundary* a) {
2216 if (!a->IsSymbol()) return false;
2217
2218 Range* range = a->symbol()->range();
2219 if ((range == NULL) || !range->min().IsSymbol()) return false;
2220
2221 const intptr_t offset = range->min().offset() + a->offset();
2222 if (!Smi::IsValid(offset)) {
2223 *a = RangeBoundary::OverflowedMinSmi();
2224 return true;
2225 }
2226
2227 *a = CanonicalizeBoundary(
2228 RangeBoundary::FromDefinition(range->min().symbol(), offset),
2229 RangeBoundary::OverflowedMinSmi());
2230
2231 return true;
2232 }
2233
2234
2235 RangeBoundary RangeBoundary::Min(RangeBoundary a, RangeBoundary b) {
2236 if (DependOnSameSymbol(a, b)) {
2237 return (a.offset() <= b.offset()) ? a : b;
2238 }
2239
2240 const intptr_t min_a = a.LowerBound().value();
2241 const intptr_t min_b = b.LowerBound().value();
2242
2243 return RangeBoundary::FromConstant(Utils::Minimum(min_a, min_b));
2244 }
2245
2246
2247 RangeBoundary RangeBoundary::Max(RangeBoundary a, RangeBoundary b) {
2248 if (DependOnSameSymbol(a, b)) {
2249 return (a.offset() >= b.offset()) ? a : b;
2250 }
2251
2252 const intptr_t max_a = a.UpperBound().value();
2253 const intptr_t max_b = b.UpperBound().value();
2254
2255 return RangeBoundary::FromConstant(Utils::Maximum(max_a, max_b));
2256 }
2257
2258
2098 void Definition::InferRange() { 2259 void Definition::InferRange() {
2099 ASSERT(GetPropagatedCid() == kSmiCid); // Has meaning only for smis. 2260 ASSERT(GetPropagatedCid() == kSmiCid); // Has meaning only for smis.
2100 if (range_ == NULL) { 2261 if (range_ == NULL) {
2101 range_ = Range::Unknown(); 2262 range_ = Range::Unknown();
2102 } 2263 }
2103 } 2264 }
2104 2265
2105 2266
2106 void ConstantInstr::InferRange() { 2267 void ConstantInstr::InferRange() {
2107 ASSERT(value_.IsSmi()); 2268 ASSERT(value_.IsSmi());
2108 if (range_ == NULL) { 2269 if (range_ == NULL) {
2109 intptr_t value = Smi::Cast(value_).Value(); 2270 intptr_t value = Smi::Cast(value_).Value();
2110 range_ = new Range(RangeBoundary::FromConstant(value), 2271 range_ = new Range(RangeBoundary::FromConstant(value),
2111 RangeBoundary::FromConstant(value)); 2272 RangeBoundary::FromConstant(value));
2112 } 2273 }
2113 } 2274 }
2114 2275
2115 2276
2116 void ConstraintInstr::InferRange() { 2277 void ConstraintInstr::InferRange() {
2117 Range* value_range = value()->definition()->range(); 2278 Range* value_range = value()->definition()->range();
2118 2279
2119 // Compute intersection of constraint and value ranges. 2280 RangeBoundary min;
2120 range_ = new Range( 2281 RangeBoundary max;
2121 RangeBoundary::Max(Range::ConstantMin(value_range), 2282
2122 Range::ConstantMin(constraint())), 2283 if (IsMinSmi(value_range) && !IsMinSmi(constraint())) {
2123 RangeBoundary::Min(Range::ConstantMax(value_range), 2284 min = constraint()->min();
2124 Range::ConstantMax(constraint()))); 2285 } else if (IsMinSmi(constraint()) && !IsMinSmi(value_range)) {
2286 min = value_range->min();
2287 } else if ((value_range != NULL) &&
2288 IsEqual(constraint()->min(), value_range->min())) {
2289 min = constraint()->min();
2290 } else {
2291 if (value_range != NULL) {
2292 RangeBoundary canonical_a =
2293 CanonicalizeBoundary(constraint()->min(),
2294 RangeBoundary::OverflowedMinSmi());
2295 RangeBoundary canonical_b =
2296 CanonicalizeBoundary(value_range->min(),
2297 RangeBoundary::OverflowedMinSmi());
2298
2299 do {
2300 if (DependOnSameSymbol(canonical_a, canonical_b)) {
2301 min = (canonical_a.offset() <= canonical_b.offset()) ? canonical_b
2302 : canonical_a;
2303 }
2304 } while (CanonicalizeMinBoundary(&canonical_a) ||
2305 CanonicalizeMinBoundary(&canonical_b));
2306 }
2307
2308 if (min.IsUnknown()) {
2309 min = RangeBoundary::Max(Range::ConstantMin(value_range),
2310 Range::ConstantMin(constraint()));
2311 }
2312 }
2313
2314 if (IsMaxSmi(value_range) && !IsMaxSmi(constraint())) {
2315 max = constraint()->max();
2316 } else if (IsMaxSmi(constraint()) && !IsMaxSmi(value_range)) {
2317 max = value_range->max();
2318 } else if ((value_range != NULL) &&
2319 IsEqual(constraint()->max(), value_range->max())) {
2320 max = constraint()->max();
2321 } else {
2322 if (value_range != NULL) {
2323 RangeBoundary canonical_b =
2324 CanonicalizeBoundary(value_range->max(),
2325 RangeBoundary::OverflowedMaxSmi());
2326 RangeBoundary canonical_a =
2327 CanonicalizeBoundary(constraint()->max(),
2328 RangeBoundary::OverflowedMaxSmi());
2329
2330 do {
2331 if (DependOnSameSymbol(canonical_a, canonical_b)) {
2332 max = (canonical_a.offset() <= canonical_b.offset()) ? canonical_a
2333 : canonical_b;
2334 break;
2335 }
2336 } while (CanonicalizeMaxBoundary(&canonical_a) ||
2337 CanonicalizeMaxBoundary(&canonical_b));
2338 }
2339
2340 if (max.IsUnknown()) {
2341 max = RangeBoundary::Min(Range::ConstantMax(value_range),
2342 Range::ConstantMax(constraint()));
2343 }
2344 }
2345
2346 range_ = new Range(min, max);
2125 } 2347 }
2126 2348
2127 2349
2350 void LoadFieldInstr::InferRange() {
2351 if ((range_ == NULL) &&
2352 ((recognized_kind() == MethodRecognizer::kObjectArrayLength) ||
2353 (recognized_kind() == MethodRecognizer::kImmutableArrayLength))) {
2354 range_ = new Range(RangeBoundary::FromConstant(0),
2355 RangeBoundary::FromConstant(Array::kMaxElements));
2356 return;
2357 }
2358 Definition::InferRange();
2359 }
2360
2361
2128 void PhiInstr::InferRange() { 2362 void PhiInstr::InferRange() {
2129 RangeBoundary new_min; 2363 RangeBoundary new_min;
2130 RangeBoundary new_max; 2364 RangeBoundary new_max;
2131 2365
2132 for (intptr_t i = 0; i < InputCount(); i++) { 2366 for (intptr_t i = 0; i < InputCount(); i++) {
2133 Range* input_range = InputAt(i)->definition()->range(); 2367 Range* input_range = InputAt(i)->definition()->range();
2134 if (input_range == NULL) { 2368 if (input_range == NULL) {
2135 range_ = Range::Unknown(); 2369 range_ = Range::Unknown();
2136 return; 2370 return;
2137 } 2371 }
(...skipping 14 matching lines...) Expand all
2152 ASSERT(new_min.IsUnknown() == new_max.IsUnknown()); 2386 ASSERT(new_min.IsUnknown() == new_max.IsUnknown());
2153 if (new_min.IsUnknown()) { 2387 if (new_min.IsUnknown()) {
2154 range_ = Range::Unknown(); 2388 range_ = Range::Unknown();
2155 return; 2389 return;
2156 } 2390 }
2157 2391
2158 range_ = new Range(new_min, new_max); 2392 range_ = new Range(new_min, new_max);
2159 } 2393 }
2160 2394
2161 2395
2396 static bool SymbolicSub(const RangeBoundary& a,
2397 const RangeBoundary& b,
2398 RangeBoundary* result) {
2399 if (a.IsSymbol() && b.IsConstant() && !b.Overflowed()) {
2400 const intptr_t offset = a.offset() - b.value();
2401 if (!Smi::IsValid(offset)) return false;
2402
2403 *result = RangeBoundary::FromDefinition(a.symbol(), offset);
2404 return true;
2405 }
2406 return false;
2407 }
2408
2409
2410 static bool SymbolicAdd(const RangeBoundary& a,
2411 const RangeBoundary& b,
2412 RangeBoundary* result) {
2413 if (a.IsSymbol() && b.IsConstant() && !b.Overflowed()) {
2414 const intptr_t offset = a.offset() + b.value();
2415 if (!Smi::IsValid(offset)) return false;
2416
2417 *result = RangeBoundary::FromDefinition(a.symbol(), offset);
2418 return true;
2419 } else if (b.IsSymbol() && a.IsConstant() && !a.Overflowed()) {
2420 const intptr_t offset = b.offset() + a.value();
2421 if (!Smi::IsValid(offset)) return false;
2422
2423 *result = RangeBoundary::FromDefinition(b.symbol(), offset);
2424 return true;
2425 }
2426 return false;
2427 }
2428
2429
2430 static bool IsArrayLength(Definition* defn) {
2431 LoadFieldInstr* load = defn->AsLoadField();
2432 return (load != NULL) &&
2433 ((load->recognized_kind() == MethodRecognizer::kObjectArrayLength) ||
2434 (load->recognized_kind() == MethodRecognizer::kImmutableArrayLength));
2435 }
2436
2437
2438 static bool IsLengthOf(Definition* defn, Definition* array) {
2439 return IsArrayLength(defn) &&
2440 (defn->AsLoadField()->value()->definition() == array);
2441 }
2442
2443
2162 void BinarySmiOpInstr::InferRange() { 2444 void BinarySmiOpInstr::InferRange() {
2163 Range* left_range = left()->definition()->range(); 2445 // TODO(vegorov): canonicalize BinarySmiOp to always have constant on the
2446 // right and a non-constant on the left.
2447 Definition* left_defn = left()->definition();
2448
2449 Range* left_range = left_defn->range();
2164 Range* right_range = right()->definition()->range(); 2450 Range* right_range = right()->definition()->range();
2165 2451
2166 if ((left_range == NULL) || (right_range == NULL)) { 2452 if ((left_range == NULL) || (right_range == NULL)) {
2167 range_ = new Range(RangeBoundary::MinSmi(), RangeBoundary::MaxSmi()); 2453 range_ = new Range(RangeBoundary::MinSmi(), RangeBoundary::MaxSmi());
2168 return; 2454 return;
2169 } 2455 }
2170 2456
2171 RangeBoundary new_min; 2457
2172 RangeBoundary new_max; 2458 // If left is a l
2459 RangeBoundary left_min =
2460 IsArrayLength(left_defn) ?
2461 RangeBoundary::FromDefinition(left_defn) : left_range->min();
2462
2463 RangeBoundary left_max =
2464 IsArrayLength(left_defn) ?
2465 RangeBoundary::FromDefinition(left_defn) : left_range->max();
2466
2467 RangeBoundary min;
2468 RangeBoundary max;
2173 switch (op_kind()) { 2469 switch (op_kind()) {
2174 case Token::kADD: 2470 case Token::kADD:
2175 new_min = 2471 if (!SymbolicAdd(left_min, right_range->min(), &min)) {
2176 RangeBoundary::Add(Range::ConstantMin(left_range), 2472 min =
2177 Range::ConstantMin(right_range), 2473 RangeBoundary::Add(Range::ConstantMin(left_range),
2178 RangeBoundary::OverflowedMinSmi()); 2474 Range::ConstantMin(right_range),
2179 new_max = 2475 RangeBoundary::OverflowedMinSmi());
2180 RangeBoundary::Add(Range::ConstantMax(left_range), 2476 }
2181 Range::ConstantMax(right_range), 2477
2182 RangeBoundary::OverflowedMaxSmi()); 2478 if (!SymbolicAdd(left_max, right_range->max(), &max)) {
2479 max =
2480 RangeBoundary::Add(Range::ConstantMax(right_range),
2481 Range::ConstantMax(left_range),
2482 RangeBoundary::OverflowedMaxSmi());
2483 }
2183 break; 2484 break;
2184 2485
2185 case Token::kSUB: 2486 case Token::kSUB:
2186 new_min = 2487 if (!SymbolicSub(left_min, right_range->max(), &min)) {
2187 RangeBoundary::Sub(Range::ConstantMin(left_range), 2488 min =
2188 Range::ConstantMax(right_range), 2489 RangeBoundary::Sub(Range::ConstantMin(left_range),
2189 RangeBoundary::OverflowedMinSmi()); 2490 Range::ConstantMax(right_range),
2190 new_max = 2491 RangeBoundary::OverflowedMinSmi());
2191 RangeBoundary::Sub(Range::ConstantMax(left_range), 2492 }
2192 Range::ConstantMin(right_range), 2493
2193 RangeBoundary::OverflowedMaxSmi()); 2494 if (!SymbolicSub(left_max, right_range->min(), &max)) {
2495 max =
2496 RangeBoundary::Sub(Range::ConstantMax(left_range),
2497 Range::ConstantMin(right_range),
2498 RangeBoundary::OverflowedMaxSmi());
2499 }
2194 break; 2500 break;
2195 2501
2196 default: 2502 default:
2197 if (range_ == NULL) { 2503 if (range_ == NULL) {
2198 range_ = Range::Unknown(); 2504 range_ = Range::Unknown();
2199 } 2505 }
2200 return; 2506 return;
2201 } 2507 }
2202 2508
2203 ASSERT(!new_min.IsUnknown() && !new_max.IsUnknown()); 2509 ASSERT(!min.IsUnknown() && !max.IsUnknown());
2204 set_overflow(new_min.Overflowed() || new_max.Overflowed()); 2510 set_overflow(min.LowerBound().Overflowed() || max.UpperBound().Overflowed());
2205 2511
2206 range_ = new Range(new_min.Clamp(), new_max.Clamp()); 2512 if (min.IsConstant()) min.Clamp();
2513 if (max.IsConstant()) max.Clamp();
2514
2515 range_ = new Range(min, max);
2207 } 2516 }
2208 2517
2209 2518
2519 bool CheckArrayBoundInstr::IsRedundant() {
2520 // Check that array has an immutable length.
2521 if ((array_type() != kArrayCid) && (array_type() != kImmutableArrayCid)) {
2522 return false;
2523 }
2524
2525 Range* index_range = index()->definition()->range();
2526
2527 // Range of the index is unknown can't decide if the check is redundant.
2528 if (index_range == NULL) return false;
2529
2530 // Range of the index is not positive. Check can't be redundant.
2531 if (Range::ConstantMin(index_range).value() < 0) return false;
2532
2533 RangeBoundary max = CanonicalizeBoundary(index_range->max(),
2534 RangeBoundary::OverflowedMaxSmi());
2535 do {
2536 if (max.IsSymbol() &&
2537 (max.offset() < 0) &&
2538 IsLengthOf(max.symbol(), array()->definition())) {
2539 return true;
2540 }
2541 } while (CanonicalizeMaxBoundary(&max));
2542
2543 // Failed to prove that maximum is bounded with array length.
2544 return false;
2545 }
2546
2547
2210 #undef __ 2548 #undef __
2211 2549
2212 } // namespace dart 2550 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/intermediate_language.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698