@@ -552,34 +552,47 @@ private module BoundsEstimate {
552552 private float nrOfBoundsPhiGuard ( RangeSsaDefinition def , StackVariable v ) {
553553 // If we have
554554 //
555- // if (x < c) { e1 }
556- // e2
555+ // if (x < c) { e1 } else { e2 }
556+ // e3
557557 //
558- // then `e2` is both a guard phi node (guarded by `x < c`) and a normal
559- // phi node (control is merged after the `if` statement).
558+ // then `{ e1 }` and `{ e2 }` are both guard phi nodes guarded by `x < c`.
559+ // The range analysis propagates bounds on `x` into both branches, filtered
560+ // by the condition. In this case all lower bounds flow to `{ e1 }` and only
561+ // lower bounds that are smaller than `c` flow to `{ e2 }`.
560562 //
561- // Assume `x` has `n` bounds. Then `n` bounds are propagated to the guard
562- // phi node `{ e1 }` and, since `{ e1 }` is input to `e2` as a normal phi
563- // node, `n` bounds are propagated to `e2`. If we also propagate the `n`
564- // bounds to `e2` as a guard phi node, then we square the number of
565- // bounds.
563+ // The largest number of bounds possible for `e3` is the number of bounds on `x` plus
564+ // one. This happens when all bounds flow from `x` to `e1` to `e3` and the
565+ // bound `c` can flow to `e2` to `e3`.
566566 //
567- // However in practice `x < c` is going to cut down the number of bounds:
568- // The tracked bounds can't flow to both branches as that would require
569- // them to simultaneously be greater and smaller than `c`. To approximate
570- // this better, the contribution from a guard phi node that is also a
571- // normal phi node is 1.
572- exists ( def .getAPhiInput ( v ) ) and
573- isGuardPhiWithBound ( def , v , _) and
574- result = 1
575- or
576- not exists ( def .getAPhiInput ( v ) ) and
577- // If there's different `access`es, then they refer to the same variable
578- // with the same lower bounds. Hence adding these guards make no sense (the
579- // implementation will take the union, but they'll be removed by
580- // deduplication). Hence we use `max` as an approximation.
581- result =
582- max ( VariableAccess access | isGuardPhiWithBound ( def , v , access ) | nrOfBoundsExpr ( access ) )
567+ // We want to optimize our bounds estimate for `e3`, as that is the estimate
568+ // that can continue propagating forward. We don't know how the existing
569+ // bounds will be split between the different branches. That depends on
570+ // whether the range analysis is tracking lower bounds or upper bounds, and
571+ // on the meaning of the condition.
572+ //
573+ // As a heuristic we divide the number of bounds on `x` by 2 to "average"
574+ // the effect of the condition and add 1 to account for the bound from the
575+ // condition itself. This will approximate estimates inside the branches,
576+ // but will give a good estimate after the branches are merged.
577+ //
578+ // This also handles cases such as this one
579+ //
580+ // if (x < c) { e1 }
581+ // e3
582+ //
583+ // where `e3` is both a guard phi node (guarded by `x < c`) and a normal
584+ // phi node (control is merged after the `if` statement). Here half of the
585+ // bounds flow into the branch and then to `e3` as a normal phi node and the
586+ // "other" half flow from the condition to `e3` as a guard phi node.
587+ exists ( float varBounds |
588+ // If there's different `access`es, then they refer to the same
589+ // variable with the same lower bounds. Hence adding these guards makes no
590+ // sense (the implementation will take the union, but they'll be removed by
591+ // deduplication). Hence we use `max` as an approximation.
592+ varBounds =
593+ max ( VariableAccess access | isGuardPhiWithBound ( def , v , access ) | nrOfBoundsExpr ( access ) ) and
594+ result = ( varBounds + 1 ) / 2
595+ )
583596 or
584597 def .isPhiNode ( v ) and
585598 not isGuardPhiWithBound ( def , v , _) and
@@ -2180,6 +2193,16 @@ module SimpleRangeAnalysisInternal {
21802193
21812194 /** Gets the estimate of the number of bounds for `e`. */
21822195 float estimateNrOfBounds ( Expr e ) { result = BoundsEstimate:: nrOfBoundsExpr ( e ) }
2196+
2197+ /** Counts the numbers of lower bounds that are computed internally for `e`. */
2198+ float countNrOfLowerBounds ( Expr e ) {
2199+ result = strictcount ( float lb | lb = getLowerBoundsImpl ( e ) | lb )
2200+ }
2201+
2202+ /** Counts the numbers of upper bounds that are computed internally for `e`. */
2203+ float countNrOfUpperBounds ( Expr e ) {
2204+ result = strictcount ( float ub | ub = getUpperBoundsImpl ( e ) | ub )
2205+ }
21832206}
21842207
21852208/** Provides predicates for debugging the simple range analysis library. */
@@ -2208,7 +2231,7 @@ private module Debug {
22082231 */
22092232 predicate countGetLowerBoundsImpl ( Expr e , int n ) {
22102233 e = getRelevantLocatable ( ) and
2211- n = strictcount ( float lb | lb = getLowerBoundsImpl ( e ) | lb )
2234+ n = SimpleRangeAnalysisInternal :: countNrOfLowerBounds ( e )
22122235 }
22132236
22142237 float debugNrOfBounds ( Expr e ) {
0 commit comments