Auto merge of #117359 - tmiasko:call-def, r=cjgillot
Fix def-use check for call terminators Fixes #117331.
This commit is contained in:
commit
6d069a0ac7
@ -36,7 +36,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
|
|
||||||
// Arguments get assigned to by means of the function being called
|
// Arguments get assigned to by means of the function being called
|
||||||
for arg in mir.args_iter() {
|
for arg in mir.args_iter() {
|
||||||
analyzer.assign(arg, DefLocation::Argument);
|
analyzer.define(arg, DefLocation::Argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there exists a local definition that dominates all uses of that local,
|
// If there exists a local definition that dominates all uses of that local,
|
||||||
@ -74,7 +74,7 @@ struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
|
impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
|
||||||
fn assign(&mut self, local: mir::Local, location: DefLocation) {
|
fn define(&mut self, local: mir::Local, location: DefLocation) {
|
||||||
let kind = &mut self.locals[local];
|
let kind = &mut self.locals[local];
|
||||||
match *kind {
|
match *kind {
|
||||||
LocalKind::ZST => {}
|
LocalKind::ZST => {}
|
||||||
@ -162,7 +162,7 @@ fn visit_assign(
|
|||||||
debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);
|
debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);
|
||||||
|
|
||||||
if let Some(local) = place.as_local() {
|
if let Some(local) = place.as_local() {
|
||||||
self.assign(local, DefLocation::Body(location));
|
self.define(local, DefLocation::Assignment(location));
|
||||||
if self.locals[local] != LocalKind::Memory {
|
if self.locals[local] != LocalKind::Memory {
|
||||||
let decl_span = self.fx.mir.local_decls[local].source_info.span;
|
let decl_span = self.fx.mir.local_decls[local].source_info.span;
|
||||||
if !self.fx.rvalue_creates_operand(rvalue, decl_span) {
|
if !self.fx.rvalue_creates_operand(rvalue, decl_span) {
|
||||||
@ -183,9 +183,14 @@ fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, locat
|
|||||||
|
|
||||||
fn visit_local(&mut self, local: mir::Local, context: PlaceContext, location: Location) {
|
fn visit_local(&mut self, local: mir::Local, context: PlaceContext, location: Location) {
|
||||||
match context {
|
match context {
|
||||||
PlaceContext::MutatingUse(MutatingUseContext::Call)
|
PlaceContext::MutatingUse(MutatingUseContext::Call) => {
|
||||||
| PlaceContext::MutatingUse(MutatingUseContext::Yield) => {
|
let call = location.block;
|
||||||
self.assign(local, DefLocation::Body(location));
|
let TerminatorKind::Call { target, .. } =
|
||||||
|
self.fx.mir.basic_blocks[call].terminator().kind
|
||||||
|
else {
|
||||||
|
bug!()
|
||||||
|
};
|
||||||
|
self.define(local, DefLocation::CallReturn { call, target });
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaceContext::NonUse(_)
|
PlaceContext::NonUse(_)
|
||||||
@ -237,6 +242,8 @@ fn visit_local(&mut self, local: mir::Local, context: PlaceContext, location: Lo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1611,14 +1611,29 @@ pub fn dominates(&self, other: Location, dominators: &Dominators<BasicBlock>) ->
|
|||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum DefLocation {
|
pub enum DefLocation {
|
||||||
Argument,
|
Argument,
|
||||||
Body(Location),
|
Assignment(Location),
|
||||||
|
CallReturn { call: BasicBlock, target: Option<BasicBlock> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefLocation {
|
impl DefLocation {
|
||||||
pub fn dominates(self, location: Location, dominators: &Dominators<BasicBlock>) -> bool {
|
pub fn dominates(self, location: Location, dominators: &Dominators<BasicBlock>) -> bool {
|
||||||
match self {
|
match self {
|
||||||
DefLocation::Argument => true,
|
DefLocation::Argument => true,
|
||||||
DefLocation::Body(def) => def.successor_within_block().dominates(location, dominators),
|
DefLocation::Assignment(def) => {
|
||||||
|
def.successor_within_block().dominates(location, dominators)
|
||||||
|
}
|
||||||
|
DefLocation::CallReturn { target: None, .. } => false,
|
||||||
|
DefLocation::CallReturn { call, target: Some(target) } => {
|
||||||
|
// The definition occurs on the call -> target edge. The definition dominates a use
|
||||||
|
// if and only if the edge is on all paths from the entry to the use.
|
||||||
|
//
|
||||||
|
// Note that a call terminator has only one edge that can reach the target, so when
|
||||||
|
// the call strongly dominates the target, all paths from the entry to the target
|
||||||
|
// go through the call -> target edge.
|
||||||
|
call != target
|
||||||
|
&& dominators.dominates(call, target)
|
||||||
|
&& dominators.dominates(target, location.block)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,8 @@ pub fn new<'tcx>(body: &Body<'tcx>) -> SsaLocals {
|
|||||||
let dominators = body.basic_blocks.dominators();
|
let dominators = body.basic_blocks.dominators();
|
||||||
|
|
||||||
let direct_uses = IndexVec::from_elem(0, &body.local_decls);
|
let direct_uses = IndexVec::from_elem(0, &body.local_decls);
|
||||||
let mut visitor = SsaVisitor { assignments, assignment_order, dominators, direct_uses };
|
let mut visitor =
|
||||||
|
SsaVisitor { body, assignments, assignment_order, dominators, direct_uses };
|
||||||
|
|
||||||
for local in body.args_iter() {
|
for local in body.args_iter() {
|
||||||
visitor.assignments[local] = Set1::One(DefLocation::Argument);
|
visitor.assignments[local] = Set1::One(DefLocation::Argument);
|
||||||
@ -110,7 +111,7 @@ pub fn assignments<'a, 'tcx>(
|
|||||||
body: &'a Body<'tcx>,
|
body: &'a Body<'tcx>,
|
||||||
) -> impl Iterator<Item = (Local, &'a Rvalue<'tcx>, Location)> + 'a {
|
) -> impl Iterator<Item = (Local, &'a Rvalue<'tcx>, Location)> + 'a {
|
||||||
self.assignment_order.iter().filter_map(|&local| {
|
self.assignment_order.iter().filter_map(|&local| {
|
||||||
if let Set1::One(DefLocation::Body(loc)) = self.assignments[local] {
|
if let Set1::One(DefLocation::Assignment(loc)) = self.assignments[local] {
|
||||||
let stmt = body.stmt_at(loc).left()?;
|
let stmt = body.stmt_at(loc).left()?;
|
||||||
// `loc` must point to a direct assignment to `local`.
|
// `loc` must point to a direct assignment to `local`.
|
||||||
let Some((target, rvalue)) = stmt.kind.as_assign() else { bug!() };
|
let Some((target, rvalue)) = stmt.kind.as_assign() else { bug!() };
|
||||||
@ -134,21 +135,21 @@ pub fn for_each_assignment_mut<'tcx>(
|
|||||||
AssignedValue::Arg,
|
AssignedValue::Arg,
|
||||||
Location { block: START_BLOCK, statement_index: 0 },
|
Location { block: START_BLOCK, statement_index: 0 },
|
||||||
),
|
),
|
||||||
Set1::One(DefLocation::Body(loc)) => {
|
Set1::One(DefLocation::Assignment(loc)) => {
|
||||||
let bb = &mut basic_blocks[loc.block];
|
let bb = &mut basic_blocks[loc.block];
|
||||||
let value = if loc.statement_index < bb.statements.len() {
|
// `loc` must point to a direct assignment to `local`.
|
||||||
// `loc` must point to a direct assignment to `local`.
|
let stmt = &mut bb.statements[loc.statement_index];
|
||||||
let stmt = &mut bb.statements[loc.statement_index];
|
let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else {
|
||||||
let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else {
|
bug!()
|
||||||
bug!()
|
|
||||||
};
|
|
||||||
assert_eq!(target.as_local(), Some(local));
|
|
||||||
AssignedValue::Rvalue(rvalue)
|
|
||||||
} else {
|
|
||||||
let term = bb.terminator_mut();
|
|
||||||
AssignedValue::Terminator(&mut term.kind)
|
|
||||||
};
|
};
|
||||||
f(local, value, loc)
|
assert_eq!(target.as_local(), Some(local));
|
||||||
|
f(local, AssignedValue::Rvalue(rvalue), loc)
|
||||||
|
}
|
||||||
|
Set1::One(DefLocation::CallReturn { call, .. }) => {
|
||||||
|
let bb = &mut basic_blocks[call];
|
||||||
|
let loc = Location { block: call, statement_index: bb.statements.len() };
|
||||||
|
let term = bb.terminator_mut();
|
||||||
|
f(local, AssignedValue::Terminator(&mut term.kind), loc)
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -201,14 +202,15 @@ pub fn meet_copy_equivalence(&self, property: &mut BitSet<Local>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SsaVisitor<'a> {
|
struct SsaVisitor<'tcx, 'a> {
|
||||||
|
body: &'a Body<'tcx>,
|
||||||
dominators: &'a Dominators<BasicBlock>,
|
dominators: &'a Dominators<BasicBlock>,
|
||||||
assignments: IndexVec<Local, Set1<DefLocation>>,
|
assignments: IndexVec<Local, Set1<DefLocation>>,
|
||||||
assignment_order: Vec<Local>,
|
assignment_order: Vec<Local>,
|
||||||
direct_uses: IndexVec<Local, u32>,
|
direct_uses: IndexVec<Local, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SsaVisitor<'_> {
|
impl SsaVisitor<'_, '_> {
|
||||||
fn check_dominates(&mut self, local: Local, loc: Location) {
|
fn check_dominates(&mut self, local: Local, loc: Location) {
|
||||||
let set = &mut self.assignments[local];
|
let set = &mut self.assignments[local];
|
||||||
let assign_dominates = match *set {
|
let assign_dominates = match *set {
|
||||||
@ -224,7 +226,7 @@ fn check_dominates(&mut self, local: Local, loc: Location) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> {
|
impl<'tcx> Visitor<'tcx> for SsaVisitor<'tcx, '_> {
|
||||||
fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) {
|
fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) {
|
||||||
match ctxt {
|
match ctxt {
|
||||||
PlaceContext::MutatingUse(MutatingUseContext::Projection)
|
PlaceContext::MutatingUse(MutatingUseContext::Projection)
|
||||||
@ -250,9 +252,18 @@ fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) {
|
|||||||
|
|
||||||
fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, loc: Location) {
|
fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, loc: Location) {
|
||||||
let location = match ctxt {
|
let location = match ctxt {
|
||||||
PlaceContext::MutatingUse(
|
PlaceContext::MutatingUse(MutatingUseContext::Store) => {
|
||||||
MutatingUseContext::Store | MutatingUseContext::Call | MutatingUseContext::Yield,
|
Some(DefLocation::Assignment(loc))
|
||||||
) => Some(DefLocation::Body(loc)),
|
}
|
||||||
|
PlaceContext::MutatingUse(MutatingUseContext::Call) => {
|
||||||
|
let call = loc.block;
|
||||||
|
let TerminatorKind::Call { target, .. } =
|
||||||
|
self.body.basic_blocks[call].terminator().kind
|
||||||
|
else {
|
||||||
|
bug!()
|
||||||
|
};
|
||||||
|
Some(DefLocation::CallReturn { call, target })
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(location) = location
|
if let Some(location) = location
|
||||||
@ -359,7 +370,7 @@ pub(crate) fn new(
|
|||||||
for (statement_index, statement) in bbdata.statements.iter().enumerate() {
|
for (statement_index, statement) in bbdata.statements.iter().enumerate() {
|
||||||
if let StatementKind::StorageLive(local) = statement.kind {
|
if let StatementKind::StorageLive(local) = statement.kind {
|
||||||
storage_live[local]
|
storage_live[local]
|
||||||
.insert(DefLocation::Body(Location { block, statement_index }));
|
.insert(DefLocation::Assignment(Location { block, statement_index }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
tests/ui/mir/ssa_call_ret.rs
Normal file
30
tests/ui/mir/ssa_call_ret.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Regression test for issue #117331, where variable `a` was misidentified as
|
||||||
|
// being in SSA form (the definition occurs on the return edge only).
|
||||||
|
//
|
||||||
|
// edition:2021
|
||||||
|
// compile-flags: --crate-type=lib
|
||||||
|
// build-pass
|
||||||
|
// needs-unwind
|
||||||
|
#![feature(custom_mir, core_intrinsics)]
|
||||||
|
use core::intrinsics::mir::*;
|
||||||
|
|
||||||
|
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||||
|
pub fn f() -> u32 {
|
||||||
|
mir!(
|
||||||
|
let a: u32;
|
||||||
|
{
|
||||||
|
Call(a = g(), bb1, UnwindCleanup(bb2))
|
||||||
|
}
|
||||||
|
bb1 = {
|
||||||
|
RET = a;
|
||||||
|
Return()
|
||||||
|
}
|
||||||
|
bb2 (cleanup) = {
|
||||||
|
RET = a;
|
||||||
|
UnwindResume()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn g() -> u32 { 0 }
|
Loading…
Reference in New Issue
Block a user