Skip to content

gccrs: Generate backend drop flags for conditional moves - #4798

Open
Lishin1215 wants to merge 2 commits into
Rust-GCC:masterfrom
Lishin1215:bir-cfg-conditional-drop-backend
Open

gccrs: Generate backend drop flags for conditional moves#4798
Lishin1215 wants to merge 2 commits into
Rust-GCC:masterfrom
Lishin1215:bir-cfg-conditional-drop-backend

Conversation

@Lishin1215

Copy link
Copy Markdown
Contributor

This patch connects the conditional BIR Drop analysis to the existing backend cleanup.

For now, in a conditional move case like:

fn f(cond: bool) {
    let x = Droppable { value: 1 };


    if cond {
        let y = x;
    }
}

The BIR Drop analysis classifies the cleanup of x as:

Drop(x): Conditional

The backend then creates a Drop flag for x.
Conceptually, the generated backend code behaves like:

try {
    bool flag = false;
    let x = Droppable { value: 1 };
    flag = true;


    if (cond) {
        let y = x;
        flag = false;
    }
}
finally {
    if (flag) {
        flag = false;
        Drop(x);
    }
}
  • When x is initialized, the flag is set to true.
  • When x is moved, the flag is cleared to false, so Drop(x) is skipped during cleanup.
  • When x is not moved, the flag remains true, so Drop(x) is called.


private:
std::set<HirId> definitely_dead;
std::set<HirId> conditionally_dropped;

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 added conditionally_dropped to save those that are classified as Conditional.
I need this information to determine whether a variable requires a Drop flag.
It is used when generating the backend code.

private:
std::set<HirId> definitely_dead;
std::set<HirId> conditionally_dropped;
std::map<HirId, HirId> move_sources;

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 added move_sources to map the move expression HirId to the source local HirId.

Since the move expression x and the source local x have different HirIds.
And also I need the source local x's HirId to find the Drop flag,
so this map lets me find the source HirId from the move expression HirId.

For example:

...
  let x = Droppable();  // source local HirId = 10
  x.flag = true;  // associate with source local HirId 10
...
  let y = x;  // move expression HirId = 30

Here I only have HirId 30, but the flag is associated with HirId 10.
So I need:

  move_sources[30] = 10

Then the backend can use HirId 10 to find the correct flag and clear it:

x.flag = false;

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.

What if multiple id are moved within the same location ? I guess any product type that move by construction would have multiple ids associated to the same hir id ?

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.

Hi, this is a good point.
The current patch only records move sources for direct whole-local moves such as let y = x.
For these moves, push_assignment() is called with a move_site.
Product type will go through a different path and do not carry a move_site, so they are not recorded in move_sources. Therefore, multiple source HirIds will not currently be associated with the same key.

I am thinking of keeping the current map and adding a comment such as:

// This may need to be extended to std::map<HirId, std::set<HirId>>
// to support product moves in the future.

Or do you think it would still be better to use a set now?
Please let me know if I misunderstood anything. Thanks!

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.

Either the comment or the change to map would be good!

Also, is there any reason why you're using map/set and not unordered_set/unordered_map ?

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.

You're right. I don't think there is any ordering requirement here.
I'll change them to std::unordered_map and std::unordered_set.
Thanks!

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've added the comment and changed std::map and std::set to std::unordered_map and std::unordered_set. Thanks!

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've now made the case you mentioned explicitly unsupported in a new commit (gccrs: Reject multiple move sources in product-type expressions)
If multiple moved IDs are associated with the same move site, gccrs emits a diagnostic using rust_sorry_at.
I've also added test! Thanks.

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.

Great! Thank you!

@Lishin1215
Lishin1215 force-pushed the bir-cfg-conditional-drop-backend branch from 90f2b12 to 6a26056 Compare August 23, 2026 21:59
@P-E-P
P-E-P self-requested a review August 23, 2026 22:49
protected: // Helpers to add BIR statements
void push_assignment (PlaceId lhs, AbstractExpr *rhs, location_t location)
void push_assignment (PlaceId lhs, AbstractExpr *rhs, location_t location,
HirId move_site = UNKNOWN_HIRID)

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.

Could we benefit from an optional ?

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.

Yes, I've changed move_site to optional now. Thanks!

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.

Thank you, I feel it makes it clearer that no value can be provided instead of relying uniquely on function overloading.

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

std::set<HirId> dead_drop_hir_ids;

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've got a feeling it would be better to pack those into a class / structure ? This way we could add annotate_drop_statement and record_drop_label as member functions and avoid carrying this state around everywhere as 4 different arguments. It would be either this if we make them member function or a single argument if we pass that struct by ref.

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've packed the four pieces of state into a DropAnalysisResults struct and now pass it by reference as a single argument!

private:
std::set<HirId> definitely_dead;
std::set<HirId> conditionally_dropped;
std::map<HirId, HirId> move_sources;

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.

What if multiple id are moved within the same location ? I guess any product type that move by construction would have multiple ids associated to the same hir id ?

@Lishin1215
Lishin1215 force-pushed the bir-cfg-conditional-drop-backend branch 3 times, most recently from 8ff0583 to c019cd7 Compare August 28, 2026 20:21

@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.

Great work! One last nitpick and I'll merge this!

Comment on lines +24 to +25
#include <unordered_map>
#include <unordered_set>

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.

You cannot rely on system import, you instead need to rely on rust-system.h

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.

Thanks for pointing this out!
I've updated it to rely on rust-system.h.

@Lishin1215
Lishin1215 force-pushed the bir-cfg-conditional-drop-backend branch from c019cd7 to e355579 Compare August 30, 2026 12:54
Generate runtime Drop flags for Conditional Drops.
Use BIR analysis results to identify locals that need Drop flags and the moves that clear those flags.
The HIR backend sets a flag when initialization, clears it after a move, and checks it before cleanup.

gcc/rust/ChangeLog:

	* backend/rust-compile-context.h
	(Context::insert_drop_flag): New function.
	(Context::lookup_drop_flag): Likewise.
	(Context::drop_flags): New member.
	* backend/rust-compile-drop-builder.cc
	(DropBuilder::maybe_create_drop_flag): New function.
	(DropBuilder::drop_flag_assignment): Likewise.
	* backend/rust-compile-drop-builder.h
	(DropBuilder::maybe_create_drop_flag): New declaration.
	(DropBuilder::drop_flag_assignment): Likewise.
	* backend/rust-compile-drop.cc
	(CompileDrop::build_current_scope_drop_cleanup): Check Drop flags before
	running conditional Drops.
	* backend/rust-compile-pattern.cc
	(CompilePatternLet::visit): Set the Drop flag after initialization.
	* backend/rust-compile-stmt.cc
	(CompileStmt::visit): Create Drop flags and clear them after moves.
	* checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
	(ExprStmtBuilder::visit): Pass expression HirIds to BIR.
	* checks/errors/borrowck/rust-bir-builder-internal.h
	(AbstractBuilder::push_assignment): Pass move-site HirIds.
	(AbstractExprBuilder::return_place): Likewise.
	* checks/errors/borrowck/rust-bir-drop-analysis.cc
	(is_straight_line): Remove.
	(record_drop_for_backend): Rename from
	record_drop_for_straight_line_backend.
	(annotate_drop_statements): Record conditional Drops and move
	sources.
	(DropAnalysis::clear): Clear the new analysis results.
	(DropAnalysis::needs_drop_flag): New function.
	(DropAnalysis::lookup_move_source): Likewise.
	(DropAnalysis::analyze): Record results for the backend.
	* checks/errors/borrowck/rust-bir-drop-analysis.h
	(DropAnalysis::needs_drop_flag): New declaration.
	(DropAnalysis::lookup_move_source): Likewise.
	(DropAnalysis::conditionally_dropped): New member.
	(DropAnalysis::move_sources): Likewise.
	* checks/errors/borrowck/rust-bir.h
	(Statement::make_assignment): Accept a move-site HirId.
	(Statement::Statement): Likewise.
	(Statement::get_move_site): New function.
	(Statement::move_site): New member.

gcc/testsuite/ChangeLog:

	* rust/execute/drop-conditional-move.rs: New test.
Pass the struct expression HIR ID through BIR so moves from the same
expression can be identified. For now, report the case as unsupported
when an expression moves more than one value.

gcc/rust/ChangeLog:

	* checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
	(ExprStmtBuilder::visit): Pass the struct expression HIR ID.
	* checks/errors/borrowck/rust-bir-builder-internal.h
	(AbstractBuilder::push_tmp_assignment): Propagate move sites.
	(AbstractBuilder::move_place): Likewise.
	(AbstractBuilder::move_all): Likewise.
	* checks/errors/borrowck/rust-bir-drop-analysis.cc
	(annotate_drop_statements): Reject multiple move sources.

gcc/testsuite/ChangeLog:

	* rust/compile/drop-conditional-product-move.rs: New test.

Signed-off-by: Lishin <lishin1008@gmail.com>
@Lishin1215
Lishin1215 force-pushed the bir-cfg-conditional-drop-backend branch from e355579 to 0fe4006 Compare August 30, 2026 12:58
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