diff options
| author | bors <bors@rust-lang.org> | 2023-09-01 10:54:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-09-01 10:54:56 +0000 |
| commit | f4555ef5e14e8f0630fc5ad4e8efaef56d4acd4b (patch) | |
| tree | effb3c6b2888a0468816146264748f65c46d9407 /compiler | |
| parent | d6b4d35d5e94f6a1577e0e645fb23afc84615a6c (diff) | |
| parent | 67553e8a11837c4b18657eedfcc272057e532bad (diff) | |
| download | rust-f4555ef5e14e8f0630fc5ad4e8efaef56d4acd4b.tar.gz rust-f4555ef5e14e8f0630fc5ad4e8efaef56d4acd4b.zip | |
Auto merge of #111752 - dingxiangfei2009:lower-or-pattern, r=cjgillot
Lower `Or` pattern without allocating place 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 the `as_temp` machinery, and plus avoid allocating the place to hold the boolean result as well.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_mir_build/src/build/expr/into.rs | 68 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/build/matches/mod.rs | 44 |
2 files changed, 74 insertions, 38 deletions
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index a5c86e31a29..1e0a47ead8c 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -159,52 +159,44 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } ExprKind::LogicalOp { op, lhs, rhs } => { - // And: - // - // [block: If(lhs)] -true-> [else_block: dest = (rhs)] - // | (false) - // [shortcircuit_block: dest = false] - // - // Or: - // - // [block: If(lhs)] -false-> [else_block: dest = (rhs)] - // | (true) - // [shortcircuit_block: dest = true] - - let (shortcircuit_block, mut else_block, join_block) = ( - this.cfg.start_new_block(), - this.cfg.start_new_block(), - this.cfg.start_new_block(), - ); - - let lhs = unpack!(block = this.as_local_operand(block, &this.thir[lhs])); - let blocks = match op { - LogicalOp::And => (else_block, shortcircuit_block), - LogicalOp::Or => (shortcircuit_block, else_block), + let condition_scope = this.local_scope(); + let source_info = this.source_info(expr.span); + // We first evaluate the left-hand side of the predicate ... + let (then_block, else_block) = + this.in_if_then_scope(condition_scope, expr.span, |this| { + this.then_else_break( + block, + &this.thir[lhs], + Some(condition_scope), + condition_scope, + source_info, + ) + }); + let (short_circuit, continuation, constant) = match op { + LogicalOp::And => (else_block, then_block, false), + LogicalOp::Or => (then_block, else_block, true), }; - let term = TerminatorKind::if_(lhs, blocks.0, blocks.1); - this.cfg.terminate(block, source_info, term); - + // At this point, the control flow splits into a short-circuiting path + // and a continuation path. + // - If the operator is `&&`, passing `lhs` leads to continuation of evaluation on `rhs`; + // failing it leads to the short-circuting path which assigns `false` to the place. + // - If the operator is `||`, failing `lhs` leads to continuation of evaluation on `rhs`; + // passing it leads to the short-circuting path which assigns `true` to the place. this.cfg.push_assign_constant( - shortcircuit_block, + short_circuit, source_info, destination, Constant { - span: expr_span, + span: expr.span, user_ty: None, - literal: match op { - LogicalOp::And => ConstantKind::from_bool(this.tcx, false), - LogicalOp::Or => ConstantKind::from_bool(this.tcx, true), - }, + literal: ConstantKind::from_bool(this.tcx, constant), }, ); - this.cfg.goto(shortcircuit_block, source_info, join_block); - - let rhs = unpack!(else_block = this.as_local_operand(else_block, &this.thir[rhs])); - this.cfg.push_assign(else_block, source_info, destination, Rvalue::Use(rhs)); - this.cfg.goto(else_block, source_info, join_block); - - join_block.unit() + let rhs = unpack!(this.expr_into_dest(destination, continuation, &this.thir[rhs])); + let target = this.cfg.start_new_block(); + this.cfg.goto(rhs, source_info, target); + this.cfg.goto(short_circuit, source_info, target); + target.unit() } ExprKind::Loop { body } => { // [block] diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 3c450740712..5ec216cea61 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -64,6 +64,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { rhs_then_block.unit() } + ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => { + let local_scope = this.local_scope(); + let (lhs_success_block, failure_block) = + this.in_if_then_scope(local_scope, expr_span, |this| { + this.then_else_break( + block, + &this.thir[lhs], + temp_scope_override, + local_scope, + variable_source_info, + ) + }); + let rhs_success_block = unpack!(this.then_else_break( + failure_block, + &this.thir[rhs], + temp_scope_override, + break_scope, + variable_source_info, + )); + this.cfg.goto(lhs_success_block, variable_source_info, rhs_success_block); + rhs_success_block.unit() + } + ExprKind::Unary { op: UnOp::Not, arg } => { + let local_scope = this.local_scope(); + let (success_block, failure_block) = + this.in_if_then_scope(local_scope, expr_span, |this| { + this.then_else_break( + block, + &this.thir[arg], + temp_scope_override, + local_scope, + variable_source_info, + ) + }); + this.break_for_else(success_block, break_scope, variable_source_info); + failure_block.unit() + } ExprKind::Scope { region_scope, lint_level, value } => { let region_scope = (region_scope, this.source_info(expr_span)); this.in_scope(region_scope, lint_level, |this| { @@ -76,6 +113,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) }) } + ExprKind::Use { source } => this.then_else_break( + block, + &this.thir[source], + temp_scope_override, + break_scope, + variable_source_info, + ), ExprKind::Let { expr, ref pat } => this.lower_let_expr( block, &this.thir[expr], |
