-
Notifications
You must be signed in to change notification settings - Fork 12.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Avoid lowering code under dead SwitchInt targets
- Loading branch information
Showing
13 changed files
with
512 additions
and
334 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
use rustc_index::bit_set::BitSet; | ||
|
||
use super::*; | ||
|
||
/// Preorder traversal of a graph. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
//! A pass that simplifies branches when their condition is known. | ||
|
||
use crate::MirPass; | ||
use rustc_middle::mir::*; | ||
use rustc_middle::ty::TyCtxt; | ||
|
||
/// The lowering for `if CONST` produces | ||
/// ``` | ||
/// _1 = Const(...); | ||
/// switchInt (move _1) | ||
/// ``` | ||
/// so this pass replaces that with | ||
/// ``` | ||
/// switchInt (Const(...)) | ||
/// ``` | ||
/// so that further MIR consumers can special-case it more easily. | ||
/// | ||
/// Unlike ConstProp, this supports generic constants too, not just concrete ones. | ||
pub struct SimplifyIfConst; | ||
|
||
impl<'tcx> MirPass<'tcx> for SimplifyIfConst { | ||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | ||
for block in body.basic_blocks_mut() { | ||
simplify_assign_move_switch(tcx, block); | ||
} | ||
} | ||
} | ||
|
||
fn simplify_assign_move_switch(tcx: TyCtxt<'_>, block: &mut BasicBlockData<'_>) { | ||
let Some(Terminator { kind: TerminatorKind::SwitchInt { discr: switch_desc, .. }, .. }) = | ||
&mut block.terminator | ||
else { | ||
return; | ||
}; | ||
|
||
let &mut Operand::Move(switch_place) = &mut *switch_desc else { return }; | ||
|
||
let Some(switch_local) = switch_place.as_local() else { return }; | ||
|
||
let Some(last_statement) = block.statements.last_mut() else { return }; | ||
|
||
let StatementKind::Assign(boxed_place_rvalue) = &last_statement.kind else { return }; | ||
|
||
let Some(assigned_local) = boxed_place_rvalue.0.as_local() else { return }; | ||
|
||
if switch_local != assigned_local { | ||
return; | ||
} | ||
|
||
if !matches!(boxed_place_rvalue.1, Rvalue::Use(Operand::Constant(_))) { | ||
return; | ||
} | ||
|
||
let should_optimize = tcx.consider_optimizing(|| { | ||
format!( | ||
"SimplifyBranches - Assignment: {:?} SourceInfo: {:?}", | ||
boxed_place_rvalue, last_statement.source_info | ||
) | ||
}); | ||
|
||
if should_optimize { | ||
let Some(last_statement) = block.statements.pop() else { | ||
bug!("Somehow the statement disappeared?"); | ||
}; | ||
|
||
let StatementKind::Assign(boxed_place_rvalue) = last_statement.kind else { | ||
bug!("Somehow it's not an assignment any more?"); | ||
}; | ||
|
||
let Rvalue::Use(assigned_constant @ Operand::Constant(_)) = boxed_place_rvalue.1 else { | ||
bug!("Somehow it's not a use of a constant any more?"); | ||
}; | ||
|
||
*switch_desc = assigned_constant; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// compile-flags: -O -C no-prepopulate-passes | ||
|
||
#![crate_type = "lib"] | ||
|
||
#[no_mangle] | ||
pub fn demo_for_i32() { | ||
generic_impl::<i32>(); | ||
} | ||
|
||
// Two important things here: | ||
// - We replace the "then" block with `unreachable` to avoid linking problems | ||
// - We neither declare nor define the `big_impl` that said block "calls". | ||
|
||
// CHECK-LABEL: ; skip_mono_inside_if_false::generic_impl | ||
// CHECK: start: | ||
// CHECK-NEXT: br i1 false, label %[[THEN_BRANCH:bb[0-9]+]], label %[[ELSE_BRANCH:bb[0-9]+]] | ||
// CHECK: [[ELSE_BRANCH]]: | ||
// CHECK-NEXT: call skip_mono_inside_if_false::small_impl | ||
// CHECK: [[THEN_BRANCH]]: | ||
// CHECK-NEXT: unreachable | ||
|
||
fn generic_impl<T>() { | ||
trait MagicTrait { | ||
const IS_BIG: bool; | ||
} | ||
impl<T> MagicTrait for T { | ||
const IS_BIG: bool = std::mem::size_of::<T>() > 10; | ||
} | ||
if T::IS_BIG { | ||
big_impl::<T>(); | ||
} else { | ||
small_impl::<T>(); | ||
} | ||
} | ||
|
||
#[inline(never)] | ||
fn small_impl<T>() {} | ||
#[inline(never)] | ||
fn big_impl<T>() {} |
Oops, something went wrong.