Open
Description
#43811 introduced a redundant branch optimization, further enhanced in #46237 and #46257.
There are still more improvements possible. Here's a partial list of ideas, issues, and improvements:
- handle cases where the dominating compare is a different relop (eg
x > 0
dominatingx == 0
).- Old prototype relop implies relop
- Also perhaps leverage this logic in
fgOptimizeUncondBranchToSimpleCond
- Redundant similar Relop checks can be eliminated #72509
- JIT: one-sided inference from unrelated predicates #72979
- JIT: extend RBO partial inference to unsigned relops #75804
- handle cases where the dominating compare has different operands (eg
x > 5
dominatingx > 3
) - handle cases where the redundant compare block has side effects. This still seems beyond our reach, as it requires code duplication and so may entail rather extensive revisions of the SSA graph. In particular, if the block has non-PHI SSA defs we need to be able to find and update all downstream uses, as well as introducing necessary PHIs. If the block has SSA uses they should be rewritten via PHI disambiguation (which as noted below can fail for some preds). Etc.
- Unoptimal codegen for "obj is T" with T being struct/sealed #36649
- JIT should propagate assertions and elide custom (bounds) checks #44040
- JIT: optimize redundant type tests created by PGO #46887
- Ineffective codegen for or pattern type checking #47920
- Some prep work in JIT: refactor jump threading to prepare for allowing side effects #60884
- Some progress towards costing/screening the IR to duplicate in main...AndyAyersMS:RboSideEffect
- Seems like in some (perhaps many) cases the side effect is a simple assignment and we can get rid of it my making forward sub more aggressive, say handling
QMARK
. We see this inType.IsInstance
, for instance.
- handle cases where the dominated or dominating compare is an internal relop (one not feeding a GT_JTRUE)
- handle cases where the relop or the dominated branch is implicit (bounds check, say). Note bounds check optimizations use conservative VNs (so that we will still be memory safe in the presence of races) while the entire redundant branch optimizer currently uses liberal VNs).
- Redundant length check when using Span<byte> => new byte[] #12571
- or perhaps consider materializing bounds checks branches before running the optimizer. This is a more radical proposal but means we might not need specialized bounds-check specific optimizations.
- use VNs more cleverly (instead of identical liberal vns, we can check if dominating exception vn set covers dominated exception vn set, and normal liberal VNs match).
- handle cases where the local compare input VN is a phi that can be destructured to reveal a known VN or a matching VN in a dominating compare
- see note below
- Inefficient recursive pattern matching (?) native code generated #37986
- JIT does not eliminate null check for "o ??= new()" #75987
- preparatory steps in
- JIT: optimize redundant branches by looking through phis #76283
- Note I am also seeing cases now where we could do this optimization, but it is not possible to map from a PHI operand to the corresponding predecessor block, because we only record one PHI operand per SSA def, not one per predecessor. So, if two predecessors bring in the same SSA def they "share" a phi operand, but it only has room for one block number, and we lose the ability to reason about the other predecessor. Not clear how much impact this has or what a plausible fix might be (other than the obvious one of having one phi input per predecessor, which can cause its own headaches if there are huge join points and lots of SSA vars).
- handle cases where dominating compare is
AND
,OR
(would guessNOT
is already handled by VN?). - run "multiple passes" as removing one redundancy can expose more
- JIT: Run optRedundantBranches twice and try to compute more accurate doms #70907
- generalize the mechanism for deciding which blocks to revisit
- remove dependence of
optJumpThread
on the basic block epoch - remove dependence of
optReachable
on the visited BB flags - Avoid generating degenerate flow during redundant branch optimization #48609
- Look more critically at the modelling of exceptions in jump threading; it seems like we are perhaps not sufficiently rigorous in the screening we do. It could be that the various exclusions in the
Check
method and elsewhere eliminate most of the places where an exception could sneak past...? - handle predicates in returns (see next item for one case of this)
- handle cases where a dominating relop could be rewritten to make a dominated relop unnecessary. See Inefficiencies with IComparer<T>.Compare vs operators #81220 for an example and some notes. A version of this is prototyped in JIT: experimental changes to redundant branch opts #83859. Also see
- Fix issue with duplicating reads: JIT: Fix RBO introducing data races #89710
- Fix issue with unreachable preds: JIT: Check for reachable preds in RBO #95556
- Generalize jump threading since fallthroughs are no longer a constraint: JIT: remove fallthrough limitations from redundant branch optimizer #97722
- Extend jump threading to allow skipping back through empty preds, transitively (see JIT: Canonicalize loop exits #98096 (comment)).
- Extend jump threading to follow back through linear flow (see RyuJIT generates unnecessary null check #4324 (comment))
- Figure out how to avoid introducing impossible results in racing programs (see JIT: RBO's utilization of liberal VNs is illegal #102158)
category:cq
theme:redundant-branches
skill-level:expert
cost:large
impact:medium