@@ -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 bound 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 make 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
0 commit comments