- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.9k
          Lower Or pattern without allocating place
          #111752
        
          New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| r? @davidtwco (rustbot has picked a reviewer for you, use r? to override) | 
      
        
              This comment has been minimized.
        
        
      
    
  This comment has been minimized.
| @dingxiangfei2009 was going to come after  | 
aabf291    to
    3863c63      
    Compare
  
    | @azizghuloum Okay, I will do the  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a few tests for this? For instance a mir-opt/built test to show the resulting MIR with temporaries to drop?
If you can, make the test a preceding commit, so we can see the diff with this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use the occasion to add a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, will do.
3863c63    to
    12d5d8f      
    Compare
  
    12d5d8f    to
    0ab73c0      
    Compare
  
    
      
        
              This comment has been minimized.
        
        
      
    
  This comment has been minimized.
0ab73c0    to
    48eefc9      
    Compare
  
    There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add the ExprKind::And, ExprKind::Use, ExprKind::Not cases too?
And also a complex one with a let condition somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do. I will also incorprate the other one from #111644.
        
          
                tests/mir-opt/building/logical_or_in_conditional.test_or.built.after.mir
              
                Outdated
          
            Show resolved
            Hide resolved
        
      48eefc9    to
    c73f139      
    Compare
  
    | I am trying another way to reduce the basic block count. | 
48643ab    to
    268c712      
    Compare
  
    
      
        
              This comment has been minimized.
        
        
      
    
  This comment has been minimized.
268c712    to
    ba91dfb      
    Compare
  
    | cc @cjgillot Good day! I am going to drop the draft status on this PR and requesting for another review. The majority of excessive block production is due to scope maintenance. To reduce this, this new patch sees a bit of overhaul of the lowering process. To assist construction of scope maintenance, a helper  As a result, for the following Rust program, if Droppy(0).0 > 0 || Droppy(1).0 > 1 {}... we have this control flow in MIR, visualised. I have to confess, this may not be the best way to do it. Any hint or pointer is very much welcome. 🙇 | 
| ☔ The latest upstream changes (presumably #112418) made this pull request unmergeable. Please resolve the merge conflicts. | 
ba91dfb    to
    9a2dc19      
    Compare
  
    | ☀️ Test successful - checks-actions | 
| LL| | if | ||
| LL| 1| ! | ||
| LL| | ! | ||
| LL| 1| is_true | ||
| LL| 0| { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a coverage perspective this seems wrong; the ! operator should have the same execution count as its operand.
However, at a glance I can't say whether this PR is actually doing something wrong, or just exposing a latent issue in the coverage instrumentor's heuristics.
(I'm not particularly concerned about this being a problem in practice; I just want to explicitly note it as something for me to keep my eye on.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be the case that because ! in if condition is now directly lowered into switchInts, by-passing the operator evaluation, the profiler might not pick it up. In other contexts, such as boolean expressions, ! should still show up I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. So if !cond { a } else { b } gets lowered as though it were if cond { b } else { a }. The ! doesn’t exist in MIR, and the coverage pass doesn’t have a special case to detect this, so it isn’t included in the coverage region.
This seems acceptable for now. The mappings are slightly worse, but they aren’t lying, and most coverage users probably won’t notice the difference.
| Finished benchmarking commit (f4555ef): comparison URL. Overall result: ❌✅ regressions and improvements - ACTION NEEDEDNext Steps: If you can justify the regressions found in this perf run, please indicate this with  @rustbot label: +perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment. 
 Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment. 
 CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment. 
 Binary sizeResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment. 
 Bootstrap: 629.909s -> 631.77s (0.30%) | 
| I also owe here an explanation about a change in codegen test  It turns out that because the emitted instructions are re-arranged, leading to the optimizer to recognize the  The IR related to this issue is attached here. | 
| Performance-wise, icounts/cycles/wall-times have a mix of wins and losses that more or less balance out. But binary size has lots of wins, which is nice. | 
| 
 @rustbot label: +perf-regression-triaged | 
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
Rollup merge of rust-lang#121784 - Zalathar:if-or-converge, r=Nadrieril Make the success arms of `if lhs || rhs` meet up in a separate block Extracted from rust-lang#118305, where this is necessary to avoid introducing a bug when injecting marker statements into the then/else arms. --- In the previous code (rust-lang#111752), the success block of `lhs` would jump directly to the success block of `rhs`. However, `rhs_success_block` could already contain statements that are specific to the RHS, and the direct goto causes them to be executed in the LHS success path as well. This patch therefore creates a fresh block that the LHS and RHS success blocks can both jump to. --- I think the reason we currently get away with this is that `rhs_success_block` usually doesn't contain anything other than StorageDead statements for locals used by the RHS, and those statements don't seem to cause problems in the LHS success path (which never makes those locals live). But if we start adding meaningful statements for branch coverage (or MC/DC coverage), it's important to keep the LHS and RHS blocks separate.
cc @azizghuloum @cjgillot
Related to #111583 and #111644
While reviewing #111644, it occurs to me that while we directly lower conjunctive predicates, which are connected with
&&, into the desirable control flow, today we don't directly lower the disjunctive predicates, which are connected with||, in the similar fashion. Instead, we allocate a place for the boolean temporary to hold the result of evaluating the||expression.Usually I would expect optimization at later stages to "inline" the evaluation of boolean predicates into simple CFG, but #111583 is an example where
&&is failing to be optimized away and the assembly shows that both the expensive operands are evaluated. Therefore, I would like to make a small change to make the CFG a bit more straight-forward without invoking theas_tempmachinery, and plus avoid allocating the place to hold the boolean result as well.