Skip to content

gccrs: Handle conditional moves in BIR drop analysis - #4777

Merged
P-E-P merged 1 commit into
Rust-GCC:masterfrom
Lishin1215:bir-cfg-conditional-drop-analysis
Aug 23, 2026
Merged

gccrs: Handle conditional moves in BIR drop analysis#4777
P-E-P merged 1 commit into
Rust-GCC:masterfrom
Lishin1215:bir-cfg-conditional-drop-analysis

Conversation

@Lishin1215

Copy link
Copy Markdown
Contributor

This patch extends the BIR Drop state analysis from straight-line control flow to conditional control flow.

It tracks whether each local variable may be initialized or uninitialized at the entry of each basic block.

Take conditional move as example,

          BB0
       initialize x
          /    \
         v      v
     BB1         BB2
    move x      no change
         \      /
          v    v
           BB3
          Drop(x)

For example,

BB0 -> BB1:
  (BB0): maybe_initialized(x)   = true
  (BB0): maybe_uninitialized(x) = false

  (BB1): reachable = false -> true
  (BB1): maybe_initialized(x)   = false -> true 
  (BB1): maybe_uninitialized(x) = false -> false 

BB1 -> BB3
  (pass through `let y = x`)
  (BB1): maybe_initialized(x) = true -> false 
  (BB1): maybe_unitialized(x) = false -> true
     
  (Assume BB2 has already reached BB3)
  (BB3): maybe_initialized(x)   = true -> true 
  (BB3): maybe_uninitialized(x) = false -> true 

BB3 -> END
  (BB3): maybe_initialized(x)   = true 
  (BB3): maybe_uninitialized(x) = true  

The analysis stops when the worklist becomes empty.


A Drop is classified as:

  1. maybe initialized only:
    1.1. Drop(x): Static
  2. maybe uninitialized only:
    2.1. Drop(x): Dead
  3. maybe initialized and maybe uninitialized:
    3.1. Drop(x): Conditional

It covers the following conditional control-flow cases:

  1. conditional move:
  let x = A { i: 1 };

  if condition {
      let y = x;
  }
  Drop(x): Conditional
  1. remains initialized after the join:
  let x = A { i: 1 };

  if condition {
      let y = 1;
  }
  Drop(x): Static
  1. moved on both branches:
  let x = A { i: 1 };

  if condition {
      let y = x;
  } else {
      let z = x;
  }
  Drop(x): Dead

Comment thread gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.cc
= compute_entry_states (function);

// Keep the existing backend handling for straight-line CFGs.
const bool record_straight_line_backend_drops = is_straight_line (function);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a specific bridge from BIR Drop analysis to the existing backend.
It stores the results using HirId and updates definitely_dead.
It is only used for straight-line CFGs because the backend does not support Conditional Drops yet.

namespace BIR {
namespace {

struct BlockInitializationState

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to merge states from different blocks, I make the state associate to block
And I added a ‘reachable’ parameter to track if a block is visited.

The previous straight-line analysis used one state while processing statements.
But in conditional cases, there are three different states that need to be represented (Static / Dead / Conditional).
The original representation could only represent two states.
So I added maybe_initialized, maybe_uninitialized to track these three states.

}

static void
update_state_for_statement (Function &function, Statement &statement,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When implementing the conditional cases, I “update initialize/uninitialized based on statements” two times in different places.

  1. The first time is because blocks need to merge states, so need to find the state and pass it to the next blocks.
  2. The second time is to use the result to decide whether a Drop is Conditional, Static, or Dead.

Therefore, I extracted this logic from the old function into a new helper.

queued[ENTRY_BASIC_BLOCK.value] = true;

// Propagate block states until the last block.
while (!worklist.empty ())

@Lishin1215 Lishin1215 Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep track of blocks that still need to be processed, I use a while loop, worklist and queued to achieve this.
So in the end

  • Block that has not been reached yet
  • Block that already reached but its state changes after merging with another state.

will be added into the worklist.

(queued is to record whether a block is already in the worklist. )

{
const size_t block_count = function.basic_blocks.size ();

for (size_t i = 0; i < block_count; i++)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I loop here again because I need to wait until the state stops changing.
And I

  1. save state
    • Because if the state might still change, the result may need to be overwritten many times. This will also increase time usage.
  2. classify/set Drop
    • Because the classification depends on the state. If the state keeps changing, the classification might be wrong.
  3. handle Straight-line backend
    • Because the DropStyle is already known, so the result can be passed to the backend.
  4. update state for statement
    • Because Drop classification needs the stable state, so need to update again here.

@Lishin1215
Lishin1215 force-pushed the bir-cfg-conditional-drop-analysis branch 2 times, most recently from 0e87c5f to ad7ea9c Compare August 16, 2026 18:55
@Lishin1215
Lishin1215 marked this pull request as ready for review August 17, 2026 05:28
@P-E-P
P-E-P self-requested a review August 18, 2026 14:00

@P-E-P P-E-P left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely familiar with BIR so it took a while to review. Another pair of eyes wouldn't hurt as all those !maybe_initialized & al are confusing sometimes but this looks good.

Comment thread gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.cc
@Lishin1215
Lishin1215 force-pushed the bir-cfg-conditional-drop-analysis branch from ad7ea9c to dd9f96d Compare August 23, 2026 18:49
@P-E-P
P-E-P enabled auto-merge August 23, 2026 19:05
Track whole-local initialization across BIR control-flow branches.

When a local is moved on only some paths, mark its Drop as conditional.
Keep the existing backend handling for straight-line CFGs and add tests
for static, dead and conditional Drops.

gcc/rust/ChangeLog:

	* checks/errors/borrowck/rust-bir-drop-analysis.cc
	(struct BlockInitializationState): New struct.
	(is_straight_line): New function.
	(set_initialized): Likewise.
	(set_uninitialized): Likewise.
	(merge_state): Likewise.
	(update_state_for_statement): Likewise.
	(classify_drop): Likewise.
	(compute_entry_states): Likewise.
	(record_drop_for_straight_line_backend): Likewise.
	(annotate_drop_statements): Likewise.
	(DropAnalysis::analyze): Propagate initialization state across the
	CFG and classify Drop statements.
	* checks/errors/borrowck/rust-bir-drop-analysis.h: Update class
	comment.

gcc/testsuite/ChangeLog:

	* rust/borrowck/drop_analysis_conditional_move.rs: New test.

Signed-off-by: Lishin <lishin1008@gmail.com>
auto-merge was automatically disabled August 23, 2026 19:21

Head branch was pushed to by a user without write access

@Lishin1215
Lishin1215 force-pushed the bir-cfg-conditional-drop-analysis branch from dd9f96d to 7e91f41 Compare August 23, 2026 19:21
@P-E-P
P-E-P added this pull request to the merge queue Aug 23, 2026
Merged via the queue into Rust-GCC:master with commit 00a99e7 Aug 23, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants