Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 library dart2js.cps_ir.bounds_checker; | |
| 6 | |
| 7 import 'cps_ir_nodes.dart'; | |
| 8 import 'optimizers.dart' show Pass; | |
| 9 import 'octagon.dart'; | |
| 10 import '../constants/values.dart'; | |
| 11 import 'cps_fragment.dart'; | |
| 12 import 'type_mask_system.dart'; | |
| 13 import '../world.dart'; | |
| 14 import '../elements/elements.dart'; | |
| 15 | |
| 16 /// Eliminates bounds checks when they can be proven safe. | |
| 17 /// | |
| 18 /// In general, this pass will try to eliminate any branch with arithmetic | |
| 19 /// in the condition, i.e. `x < y`, `x <= y`, `x == y` etc. | |
| 20 /// | |
| 21 /// The analysis uses an [Octagon] abstract domain. Unlike traditional octagon | |
| 22 /// analyzers, we do not use a closed matrix representation, but just maintain | |
| 23 /// a bucket of constraints. Constraints can therefore be added and removed | |
| 24 /// on-the-fly without significant overhead. | |
| 25 /// | |
| 26 /// We never copy the constraint system. While traversing the IR, the | |
| 27 /// constraint system is mutated to take into account the knowledge that is | |
| 28 /// valid for the current location. Constraints are added when entering a | |
| 29 /// branch, for instance, and removed again after the branch has been processed. | |
| 30 /// | |
| 31 /// Loops are analyzed in two passes. The first pass establishes monotonicity | |
| 32 /// of loop variables, which the second pass uses to compute upper/lower bounds. | |
| 33 /// The first pass also records whether any side effects occurred in the loop. | |
| 34 /// | |
| 35 /// The two-pass scheme is suboptimal compared to a least fixed-point | |
| 36 /// computation, but does not require repeated iteration. Repeated iteration | |
| 37 /// would be expensive, since we cannot perform a sparse analysis with our | |
| 38 /// mutable octagon representation. | |
| 39 class BoundsChecker extends RecursiveVisitor implements Pass { | |
| 40 String get passName => 'Bounds checker'; | |
| 41 | |
| 42 static const int MAX_UINT32 = (1 << 32) - 1; | |
| 43 | |
| 44 /// All integers of this magnitude or less are representable as JS numbers. | |
| 45 static const int MAX_SAFE_INT = (1 << 53) - 1; | |
| 46 | |
| 47 /// Marker to indicate that a continuation should get a unique effect number. | |
| 48 static const int NEW_EFFECT = -1; | |
| 49 | |
| 50 final TypeMaskSystem types; | |
| 51 final World world; | |
| 52 | |
| 53 /// Fields for the constraint system and its variables. | |
| 54 final Octagon octagon = new Octagon(); | |
| 55 final Map<Primitive, SignedVariable> valueOf = {}; | |
| 56 final Map<Primitive, Map<int, SignedVariable>> lengthOf = {}; | |
| 57 | |
| 58 /// Fields for the two-pass handling of loops. | |
| 59 final Set<Continuation> loopsWithSideEffects = new Set<Continuation>(); | |
| 60 final Map<Parameter, Monotonicity> monotonicity = <Parameter, Monotonicity>{}; | |
| 61 bool isStrongLoopPass; | |
| 62 bool foundLoop = false; | |
| 63 | |
| 64 /// Fields for tracking side effects. | |
| 65 /// | |
| 66 /// The IR is divided into regions wherein the lengths of indexable objects | |
| 67 /// are known not to change. Regions are identified by their "effect number". | |
| 68 final Map<Continuation, int> effectNumberAt = <Continuation, int>{}; | |
| 69 int currentEffectNumber = 0; | |
| 70 int effectNumberCounter = 0; | |
| 71 | |
| 72 BoundsChecker(this.types, this.world); | |
| 73 | |
| 74 void rewrite(FunctionDefinition node) { | |
| 75 isStrongLoopPass = false; | |
| 76 visit(node); | |
| 77 if (foundLoop) { | |
| 78 isStrongLoopPass = true; | |
| 79 effectNumberAt.clear(); | |
| 80 visit(node); | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 /// ------------- VARIABLES ----------------- | |
| 85 | |
| 86 int makeNewEffect() => ++effectNumberCounter; | |
| 87 | |
| 88 bool isInt(Primitive prim) { | |
| 89 return types.isDefinitelyInt(prim.type); | |
| 90 } | |
| 91 | |
| 92 bool isUInt32(Primitive prim) { | |
| 93 return types.isDefinitelyUInt32(prim.type); | |
| 94 } | |
| 95 | |
| 96 /// Get a constraint variable representing the numeric value of [number]. | |
| 97 SignedVariable getValue(Primitive number) { | |
| 98 assert(isInt(number)); | |
| 99 number = number.effectiveDefinition; | |
| 100 int min, max; | |
| 101 if (isUInt32(number)) { | |
|
sra1
2015/09/30 21:54:19
Is there a benefit to matching JSPositiveInt too?
asgerf
2015/10/01 09:49:34
Might as well match against it.
| |
| 102 min = 0; | |
| 103 max = MAX_UINT32; | |
| 104 } | |
| 105 return valueOf.putIfAbsent(number, () => octagon.makeVariable(min, max)); | |
| 106 } | |
| 107 | |
| 108 /// Get a constraint variable representing the length of [indexableObject] at | |
| 109 /// program locations with the given [effectCounter]. | |
| 110 SignedVariable getLength(Primitive indexableObject, int effectCounter) { | |
| 111 indexableObject = indexableObject.effectiveDefinition; | |
| 112 if (indexableObject.type != null && | |
| 113 types.isDefinitelyFixedLengthIndexable(indexableObject.type)) { | |
| 114 // Always use the same effect counter if the length is immutable. | |
| 115 effectCounter = 0; | |
| 116 } | |
| 117 return lengthOf | |
| 118 .putIfAbsent(indexableObject, () => <int, SignedVariable>{}) | |
| 119 .putIfAbsent(effectCounter, () => octagon.makeVariable(0, MAX_UINT32)); | |
| 120 } | |
| 121 | |
| 122 /// ------------- CONSTRAINT HELPERS ----------------- | |
|
sra1
2015/09/30 21:54:19
Use '//'.
This is a doc comment that applies to ap
asgerf
2015/10/01 09:49:33
Done.
| |
| 123 | |
| 124 // Puts the given constraint "in scope" by adding it to the octagon, and | |
| 125 // pushing a stack action that will remove it again. | |
| 126 void applyConstraint(SignedVariable v1, SignedVariable v2, int k) { | |
| 127 Constraint constraint = new Constraint(v1, v2, k); | |
| 128 octagon.pushConstraint(constraint); | |
| 129 pushAction(() => octagon.popConstraint(constraint)); | |
| 130 } | |
| 131 | |
| 132 /// Return true if we can prove that `v1 + v2 <= k`. | |
| 133 bool testConstraint(SignedVariable v1, SignedVariable v2, int k) { | |
| 134 // Add the negated constraint and check for solvability. | |
| 135 // !(v1 + v2 <= k) <==> -v1 - v2 <= -k-1 | |
| 136 Constraint constraint = new Constraint(v1.negated, v2.negated, -k - 1); | |
| 137 octagon.pushConstraint(constraint); | |
| 138 bool answer = octagon.isUnsolvable; | |
| 139 octagon.popConstraint(constraint); | |
| 140 return answer; | |
| 141 } | |
| 142 | |
| 143 void makeLessThanOrEqual(SignedVariable v1, SignedVariable v2) { | |
| 144 // v1 <= v2 <==> v1 - v2 <= 0 | |
| 145 applyConstraint(v1, v2.negated, 0); | |
| 146 } | |
| 147 | |
| 148 void makeLessThan(SignedVariable v1, SignedVariable v2) { | |
| 149 // v1 < v2 <==> v1 - v2 <= -1 | |
| 150 applyConstraint(v1, v2.negated, -1); | |
| 151 } | |
| 152 | |
| 153 void makeGreaterThanOrEqual(SignedVariable v1, SignedVariable v2) { | |
| 154 // v1 >= v2 <==> v2 - v1 <= 0 | |
| 155 applyConstraint(v2, v1.negated, 0); | |
| 156 } | |
| 157 | |
| 158 void makeGreaterThan(SignedVariable v1, SignedVariable v2) { | |
| 159 // v1 > v2 <==> v2 - v1 <= -1 | |
| 160 applyConstraint(v2, v1.negated, -1); | |
| 161 } | |
| 162 | |
| 163 void makeConstant(SignedVariable v1, int k) { | |
| 164 // We model this using the constraints: | |
| 165 // v1 + v1 <= 2k | |
| 166 // -v1 - v1 <= -2k | |
| 167 applyConstraint(v1, v1, 2 * k); | |
| 168 applyConstraint(v1.negated, v1.negated, -2 * k); | |
| 169 } | |
| 170 | |
| 171 /// Make `v1 = v2 + k`. | |
| 172 void makeExactSum(SignedVariable v1, SignedVariable v2, int k) { | |
| 173 applyConstraint(v1, v2.negated, k); | |
| 174 applyConstraint(v1.negated, v2, -k); | |
| 175 } | |
| 176 | |
| 177 /// Make `v1 = v2 [+] k` where [+] represents floating-point addition. | |
| 178 void makeFloatingPointSum(SignedVariable v1, SignedVariable v2, int k) { | |
| 179 if (isDefinitelyLessThanOrEqualToConstant(v2, MAX_SAFE_INT - k) && | |
| 180 isDefinitelyGreaterThanOrEqualToConstant(v2, -MAX_SAFE_INT + k)) { | |
| 181 // The result is known to be in the 53-bit range, so no rounding occurs. | |
| 182 makeExactSum(v1, v2, k); | |
| 183 } else { | |
| 184 // A rounding error may occur, so the result may not be exactly v2 + k. | |
| 185 // We can still add monotonicity constraints: | |
| 186 // adding a positive number cannot return a lesser number | |
| 187 // adding a negative number cannot return a greater number | |
| 188 if (k >= 0) { | |
| 189 // v1 >= v2 <==> v2 - v1 <= 0 <==> -v1 + v2 <= 0 | |
| 190 applyConstraint(v1.negated, v2, 0); | |
| 191 } else { | |
| 192 // v1 <= v2 <==> v1 - v2 <= 0 | |
| 193 applyConstraint(v1, v2.negated, 0); | |
| 194 } | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 void makeEqual(SignedVariable v1, SignedVariable v2) { | |
| 199 // We model this using the constraints: | |
| 200 // v1 <= v2 <==> v1 - v2 <= 0 | |
| 201 // v1 >= v2 <==> v2 - v1 <= 0 | |
| 202 applyConstraint(v1, v2.negated, 0); | |
| 203 applyConstraint(v2, v1.negated, 0); | |
| 204 } | |
| 205 | |
| 206 void makeNotEqual(SignedVariable v1, SignedVariable v2) { | |
| 207 // The octagon cannot represent non-equality, but we can sharpen a weak | |
| 208 // inequality to a sharp one. If v1 and v2 are already known to be equal, | |
| 209 // this will create a contradiction and eliminate a dead branch. | |
| 210 // This is necessary for eliminating concurrent modification checks. | |
| 211 if (isDefinitelyLessThanOrEqualTo(v1, v2)) { | |
| 212 makeLessThan(v1, v2); | |
| 213 } else if (isDefinitelyGreaterThanOrEqualTo(v1, v2)) { | |
| 214 makeGreaterThan(v1, v2); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 /// Return true if we can prove that `v1 <= v2`. | |
| 219 bool isDefinitelyLessThanOrEqualTo(SignedVariable v1, SignedVariable v2) { | |
| 220 return testConstraint(v1, v2.negated, 0); | |
| 221 } | |
| 222 | |
| 223 /// Return true if we can prove that `v1 >= v2`. | |
| 224 bool isDefinitelyGreaterThanOrEqualTo(SignedVariable v1, SignedVariable v2) { | |
| 225 return testConstraint(v2, v1.negated, 0); | |
| 226 } | |
| 227 | |
| 228 bool isDefinitelyLessThanOrEqualToConstant(SignedVariable v1, int value) { | |
| 229 // v1 <= value <==> v1 + v1 <= 2 * value | |
| 230 return testConstraint(v1, v1, 2 * value); | |
| 231 } | |
| 232 | |
| 233 bool isDefinitelyGreaterThanOrEqualToConstant(SignedVariable v1, int value) { | |
| 234 // v1 >= value <==> -v1 - v1 <= -2 * value | |
| 235 return testConstraint(v1.negated, v1.negated, -2 * value); | |
| 236 } | |
| 237 | |
| 238 /// ------------- TAIL EXPRESSIONS ----------------- | |
| 239 | |
| 240 @override | |
| 241 void visitBranch(Branch node) { | |
| 242 Primitive condition = node.condition.definition; | |
| 243 Continuation trueCont = node.trueContinuation.definition; | |
| 244 Continuation falseCont = node.falseContinuation.definition; | |
| 245 effectNumberAt[trueCont] = currentEffectNumber; | |
| 246 effectNumberAt[falseCont] = currentEffectNumber; | |
| 247 pushAction(() { | |
| 248 // If the branching condition is known statically, either or both of the | |
| 249 // branch continuations will be replaced by Unreachable. Clean up the | |
| 250 // branch afterwards. | |
| 251 if (trueCont.body is Unreachable && falseCont.body is Unreachable) { | |
| 252 replaceExpression(node, new Unreachable()); | |
| 253 } else if (trueCont.body is Unreachable) { | |
| 254 replaceExpression( | |
| 255 node, new InvokeContinuation(falseCont, <Parameter>[])); | |
| 256 } else if (falseCont.body is Unreachable) { | |
| 257 replaceExpression( | |
| 258 node, new InvokeContinuation(trueCont, <Parameter>[])); | |
| 259 } | |
| 260 }); | |
| 261 void pushTrue(makeConstraint()) { | |
| 262 pushAction(() { | |
| 263 makeConstraint(); | |
| 264 push(trueCont); | |
| 265 }); | |
| 266 } | |
| 267 void pushFalse(makeConstraint()) { | |
| 268 pushAction(() { | |
| 269 makeConstraint(); | |
| 270 push(falseCont); | |
| 271 }); | |
| 272 } | |
| 273 if (condition is ApplyBuiltinOperator && | |
| 274 condition.arguments.length == 2 && | |
| 275 isInt(condition.arguments[0].definition) && | |
| 276 isInt(condition.arguments[1].definition)) { | |
| 277 SignedVariable v1 = getValue(condition.arguments[0].definition); | |
| 278 SignedVariable v2 = getValue(condition.arguments[1].definition); | |
| 279 switch (condition.operator) { | |
| 280 case BuiltinOperator.NumLe: | |
| 281 pushTrue(() => makeLessThanOrEqual(v1, v2)); | |
| 282 pushFalse(() => makeGreaterThan(v1, v2)); | |
| 283 return; | |
| 284 case BuiltinOperator.NumLt: | |
| 285 pushTrue(() => makeLessThan(v1, v2)); | |
| 286 pushFalse(() => makeGreaterThanOrEqual(v1, v2)); | |
| 287 return; | |
| 288 case BuiltinOperator.NumGe: | |
| 289 pushTrue(() => makeGreaterThanOrEqual(v1, v2)); | |
| 290 pushFalse(() => makeLessThan(v1, v2)); | |
| 291 return; | |
| 292 case BuiltinOperator.NumGt: | |
| 293 pushTrue(() => makeGreaterThan(v1, v2)); | |
| 294 pushFalse(() => makeLessThanOrEqual(v1, v2)); | |
| 295 return; | |
| 296 case BuiltinOperator.StrictEq: | |
| 297 pushTrue(() => makeEqual(v1, v2)); | |
| 298 pushFalse(() => makeNotEqual(v1, v2)); | |
| 299 return; | |
| 300 case BuiltinOperator.StrictNeq: | |
| 301 pushTrue(() => makeNotEqual(v1, v2)); | |
| 302 pushFalse(() => makeEqual(v1, v2)); | |
| 303 return; | |
| 304 default: | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 push(trueCont); | |
| 309 push(falseCont); | |
| 310 } | |
| 311 | |
| 312 @override | |
| 313 void visitConstant(Constant node) { | |
| 314 // TODO(asgerf): It might be faster to inline the constant in the | |
| 315 // constraints that reference it. | |
| 316 if (node.value.isInt) { | |
| 317 IntConstantValue constant = node.value; | |
| 318 makeConstant(getValue(node), constant.primitiveValue); | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 @override | |
| 323 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | |
| 324 if (node.operator != BuiltinOperator.NumAdd && | |
| 325 node.operator != BuiltinOperator.NumSubtract) { | |
| 326 return; | |
| 327 } | |
| 328 if (!isInt(node.arguments[0].definition) || | |
| 329 !isInt(node.arguments[1].definition)) { | |
| 330 return; | |
| 331 } | |
| 332 // We have `v1 = v2 +/- v3`, but the octagon cannot represent constraints | |
| 333 // involving more than two variables. Check if one operand is a constant. | |
| 334 int getConstantArgument(int n) { | |
| 335 Primitive prim = node.arguments[n].definition; | |
| 336 if (prim is Constant && prim.value.isInt) { | |
| 337 IntConstantValue constant = prim.value; | |
| 338 return constant.primitiveValue; | |
| 339 } | |
| 340 return null; | |
| 341 } | |
| 342 int constant = getConstantArgument(0); | |
| 343 int operandIndex = 1; | |
| 344 if (constant == null) { | |
| 345 constant = getConstantArgument(1); | |
| 346 operandIndex = 0; | |
| 347 } | |
| 348 if (constant == null) { | |
| 349 // Neither argument was a constant. | |
| 350 // Classical octagon-based analyzers would compute upper and lower bounds | |
| 351 // for the two operands and add constraints for the result based on | |
| 352 // those. For performance reasons we omit that. | |
| 353 // TODO(asgerf): It seems expensive, but we should evaluate it. | |
| 354 return; | |
| 355 } | |
| 356 SignedVariable v1 = getValue(node); | |
| 357 SignedVariable v2 = getValue(node.arguments[operandIndex].definition); | |
| 358 | |
| 359 if (node.operator == BuiltinOperator.NumAdd) { | |
| 360 // v1 = v2 + const | |
| 361 makeFloatingPointSum(v1, v2, constant); | |
| 362 } else if (operandIndex == 0) { | |
| 363 // v1 = v2 - const | |
| 364 makeFloatingPointSum(v1, v2, -constant); | |
| 365 } else { | |
| 366 // v1 = const - v2 <==> v1 = (-v2) + const | |
| 367 makeFloatingPointSum(v1, v2.negated, constant); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 @override | |
| 372 void visitGetLength(GetLength node) { | |
| 373 valueOf[node] = getLength(node.object.definition, currentEffectNumber); | |
| 374 } | |
| 375 | |
| 376 void analyzeLoopEntry(InvokeContinuation node) { | |
| 377 foundLoop = true; | |
| 378 Continuation cont = node.continuation.definition; | |
| 379 if (isStrongLoopPass) { | |
| 380 for (int i = 0; i < node.arguments.length; ++i) { | |
| 381 Parameter param = cont.parameters[i]; | |
| 382 if (!isInt(param)) continue; | |
| 383 Primitive initialValue = node.arguments[i].definition; | |
| 384 SignedVariable initialVariable = getValue(initialValue); | |
| 385 Monotonicity mono = monotonicity[param]; | |
| 386 if (mono == null) { | |
| 387 // Value never changes. This is extremely uncommon. | |
| 388 initialValue.substituteFor(param); | |
| 389 } else if (mono == Monotonicity.Increasing) { | |
| 390 makeGreaterThanOrEqual(getValue(param), initialVariable); | |
| 391 } else if (mono == Monotonicity.Decreasing) { | |
| 392 makeLessThanOrEqual(getValue(param), initialVariable); | |
| 393 } | |
| 394 } | |
| 395 if (loopsWithSideEffects.contains(cont)) { | |
| 396 currentEffectNumber = makeNewEffect(); | |
| 397 } | |
| 398 } else { | |
| 399 // During the weak pass, conservatively make a new effect number in the | |
| 400 // loop body. This may be strengthened during the strong pass. | |
| 401 currentEffectNumber = effectNumberAt[cont] = makeNewEffect(); | |
| 402 } | |
| 403 push(cont); | |
| 404 } | |
| 405 | |
| 406 void analyzeLoopContinue(InvokeContinuation node) { | |
| 407 Continuation cont = node.continuation.definition; | |
| 408 | |
| 409 // During the strong loop phase, there is no need to compute monotonicity, | |
| 410 // and we already put bounds on the loop variables when we went into the | |
| 411 // loop. | |
| 412 if (isStrongLoopPass) return; | |
| 413 | |
| 414 // For each loop parameter, try to prove that the new value is definitely | |
| 415 // less/greater than its old value. When we fail to prove this, update the | |
| 416 // monotonicity flag accordingly. | |
| 417 for (int i = 0; i < node.arguments.length; ++i) { | |
| 418 Parameter param = cont.parameters[i]; | |
| 419 if (!isInt(param)) continue; | |
| 420 SignedVariable arg = getValue(node.arguments[i].definition); | |
| 421 SignedVariable paramVar = getValue(param); | |
| 422 if (!isDefinitelyLessThanOrEqualTo(arg, paramVar)) { | |
| 423 // We couldn't prove that the value does not increase, so assume | |
| 424 // henceforth that it might be increasing. | |
| 425 markMonotonicity(cont.parameters[i], Monotonicity.Increasing); | |
| 426 } | |
| 427 if (!isDefinitelyGreaterThanOrEqualTo(arg, paramVar)) { | |
| 428 // We couldn't prove that the value does not decrease, so assume | |
| 429 // henceforth that it might be decrease. | |
| 430 markMonotonicity(cont.parameters[i], Monotonicity.Decreasing); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 // If a side effect has occurred between the entry and continue, mark | |
| 435 // the loop as having side effects. | |
| 436 if (currentEffectNumber != effectNumberAt[cont]) { | |
| 437 loopsWithSideEffects.add(cont); | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 void markMonotonicity(Parameter param, Monotonicity mono) { | |
| 442 Monotonicity current = monotonicity[param]; | |
| 443 if (current == null) { | |
| 444 monotonicity[param] = mono; | |
| 445 } else if (current != mono) { | |
| 446 monotonicity[param] = Monotonicity.NotMonotone; | |
| 447 } | |
| 448 } | |
| 449 | |
| 450 @override | |
| 451 void visitInvokeContinuation(InvokeContinuation node) { | |
| 452 Continuation cont = node.continuation.definition; | |
| 453 if (node.isRecursive) { | |
| 454 analyzeLoopContinue(node); | |
| 455 } else if (cont.isRecursive) { | |
| 456 analyzeLoopEntry(node); | |
| 457 } else { | |
| 458 int effect = effectNumberAt[cont]; | |
| 459 if (effect == null) { | |
| 460 effectNumberAt[cont] = currentEffectNumber; | |
| 461 } else if (effect != currentEffectNumber && effect != NEW_EFFECT) { | |
| 462 effectNumberAt[cont] = NEW_EFFECT; | |
| 463 } | |
| 464 // TODO(asgerf): Compute join for parameters to increase precision? | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 /// ---------------- CALL EXPRESSIONS -------------------- | |
| 469 | |
| 470 @override | |
| 471 void visitInvokeMethod(InvokeMethod node) { | |
| 472 // TODO(asgerf): What we really need is a "changes length" side effect flag. | |
|
sra1
2015/09/30 21:54:20
More than that - with aliases so a.add(b[i]) does
asgerf
2015/10/01 09:49:33
Absolutely. I just wanted to clarify why we are us
| |
| 473 if (world | |
| 474 .getSideEffectsOfSelector(node.selector, node.mask) | |
| 475 .changesIndex()) { | |
| 476 currentEffectNumber = makeNewEffect(); | |
| 477 } | |
| 478 push(node.continuation.definition); | |
| 479 } | |
| 480 | |
| 481 @override | |
| 482 void visitInvokeStatic(InvokeStatic node) { | |
| 483 if (world.getSideEffectsOfElement(node.target).changesIndex()) { | |
| 484 currentEffectNumber = makeNewEffect(); | |
| 485 } | |
| 486 push(node.continuation.definition); | |
| 487 } | |
| 488 | |
| 489 @override | |
| 490 void visitInvokeMethodDirectly(InvokeMethodDirectly node) { | |
| 491 FunctionElement target = node.target; | |
| 492 if (target is ConstructorBodyElement) { | |
| 493 ConstructorBodyElement body = target; | |
| 494 target = body.constructor; | |
| 495 } | |
| 496 if (world.getSideEffectsOfElement(target).changesIndex()) { | |
| 497 currentEffectNumber = makeNewEffect(); | |
| 498 } | |
| 499 push(node.continuation.definition); | |
| 500 } | |
| 501 | |
| 502 @override | |
| 503 void visitInvokeConstructor(InvokeConstructor node) { | |
| 504 if (world.getSideEffectsOfElement(node.target).changesIndex()) { | |
| 505 currentEffectNumber = makeNewEffect(); | |
| 506 } | |
| 507 push(node.continuation.definition); | |
| 508 } | |
| 509 | |
| 510 @override | |
| 511 void visitTypeCast(TypeCast node) { | |
| 512 push(node.continuation.definition); | |
| 513 } | |
| 514 | |
| 515 @override | |
| 516 void visitGetLazyStatic(GetLazyStatic node) { | |
| 517 // TODO(asgerf): How do we get the side effects of a lazy field initializer? | |
| 518 currentEffectNumber = makeNewEffect(); | |
| 519 push(node.continuation.definition); | |
| 520 } | |
| 521 | |
| 522 @override | |
| 523 void visitForeignCode(ForeignCode node) { | |
| 524 if (node.nativeBehavior.sideEffects.changesIndex()) { | |
| 525 currentEffectNumber = makeNewEffect(); | |
| 526 } | |
| 527 push(node.continuation.definition); | |
| 528 } | |
| 529 | |
| 530 @override | |
| 531 void visitAwait(Await node) { | |
| 532 currentEffectNumber = makeNewEffect(); | |
| 533 push(node.continuation.definition); | |
| 534 } | |
| 535 | |
| 536 /// ---------------- PRIMITIVES -------------------- | |
| 537 | |
| 538 @override | |
| 539 void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { | |
| 540 Primitive receiver = node.receiver.definition; | |
| 541 int effectBefore = currentEffectNumber; | |
| 542 currentEffectNumber = makeNewEffect(); | |
| 543 int effectAfter = currentEffectNumber; | |
| 544 SignedVariable lengthBefore = getLength(receiver, effectBefore); | |
| 545 SignedVariable lengthAfter = getLength(receiver, effectAfter); | |
| 546 switch (node.method) { | |
| 547 case BuiltinMethod.Push: | |
| 548 // after = before + count | |
| 549 int count = node.arguments.length; | |
| 550 makeExactSum(lengthAfter, lengthBefore, count); | |
| 551 break; | |
| 552 | |
| 553 case BuiltinMethod.Pop: | |
| 554 // after = before - 1 | |
| 555 makeExactSum(lengthAfter, lengthBefore, -1); | |
| 556 break; | |
| 557 } | |
| 558 } | |
| 559 | |
| 560 @override | |
| 561 void visitLiteralList(LiteralList node) { | |
| 562 makeConstant(getLength(node, currentEffectNumber), node.values.length); | |
| 563 } | |
| 564 | |
| 565 /// ---------------- INTERIOR EXPRESSIONS -------------------- | |
| 566 | |
| 567 @override | |
| 568 Expression traverseContinuation(Continuation cont) { | |
| 569 if (octagon.isUnsolvable) { | |
| 570 RemovalVisitor.remove(cont.body); | |
| 571 cont.body = new Unreachable(); | |
| 572 } else { | |
| 573 int effect = effectNumberAt[cont]; | |
| 574 if (effect != null) { | |
| 575 currentEffectNumber = effect == NEW_EFFECT ? makeNewEffect() : effect; | |
| 576 } | |
| 577 } | |
| 578 return cont.body; | |
| 579 } | |
| 580 | |
| 581 @override | |
| 582 Expression traverseLetCont(LetCont node) { | |
| 583 // Join continuations should be pushed at declaration-site, so all their | |
| 584 // call sites are seen before they are analyzed. | |
| 585 // Other continuations are pushed at the use site. | |
| 586 for (Continuation cont in node.continuations) { | |
| 587 if (cont.hasAtLeastOneUse && | |
| 588 !cont.isRecursive && | |
| 589 cont.firstRef.parent is InvokeContinuation) { | |
| 590 push(cont); | |
| 591 } | |
| 592 } | |
| 593 return node.body; | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 /// Lattice representing the known monotonicity of a loop variable. | |
| 598 /// | |
| 599 /// The lattice bottom is represented by `null` and represents the case where | |
| 600 /// the loop variable never changes value during the loop. | |
|
sra1
2015/09/30 21:54:19
These are weak monotonicity, not strict, right?
asgerf
2015/10/01 09:49:33
Yes. Clarified in the comment.
| |
| 601 enum Monotonicity { NotMonotone, Increasing, Decreasing, } | |
| OLD | NEW |