Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
int
Rust
提交
c68d710d
R
Rust
项目概览
int
/
Rust
大约 1 年 前同步成功
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
Rust
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
c68d710d
编写于
3月 22, 2020
作者:
D
Dylan MacKenzie
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Support backward dataflow analyses
上级
032be94d
变更
16
展开全部
隐藏空白更改
内联
并排
Showing
16 changed file
with
1094 addition
and
661 deletion
+1094
-661
src/librustc_mir/borrow_check/mod.rs
src/librustc_mir/borrow_check/mod.rs
+3
-3
src/librustc_mir/borrow_check/type_check/liveness/trace.rs
src/librustc_mir/borrow_check/type_check/liveness/trace.rs
+2
-2
src/librustc_mir/dataflow/framework/cursor.rs
src/librustc_mir/dataflow/framework/cursor.rs
+115
-189
src/librustc_mir/dataflow/framework/direction.rs
src/librustc_mir/dataflow/framework/direction.rs
+570
-0
src/librustc_mir/dataflow/framework/engine.rs
src/librustc_mir/dataflow/framework/engine.rs
+102
-236
src/librustc_mir/dataflow/framework/graphviz.rs
src/librustc_mir/dataflow/framework/graphviz.rs
+91
-44
src/librustc_mir/dataflow/framework/mod.rs
src/librustc_mir/dataflow/framework/mod.rs
+77
-45
src/librustc_mir/dataflow/framework/tests.rs
src/librustc_mir/dataflow/framework/tests.rs
+77
-86
src/librustc_mir/dataflow/framework/visitor.rs
src/librustc_mir/dataflow/framework/visitor.rs
+38
-35
src/librustc_mir/dataflow/impls/storage_liveness.rs
src/librustc_mir/dataflow/impls/storage_liveness.rs
+2
-2
src/librustc_mir/dataflow/mod.rs
src/librustc_mir/dataflow/mod.rs
+3
-2
src/librustc_mir/lib.rs
src/librustc_mir/lib.rs
+3
-1
src/librustc_mir/transform/check_consts/validation.rs
src/librustc_mir/transform/check_consts/validation.rs
+3
-3
src/librustc_mir/transform/elaborate_drops.rs
src/librustc_mir/transform/elaborate_drops.rs
+3
-3
src/librustc_mir/transform/generator.rs
src/librustc_mir/transform/generator.rs
+4
-9
src/librustc_mir/transform/rustc_peek.rs
src/librustc_mir/transform/rustc_peek.rs
+1
-1
未找到文件。
src/librustc_mir/borrow_check/mod.rs
浏览文件 @
c68d710d
...
...
@@ -518,7 +518,7 @@ fn do_mir_borrowck<'a, 'tcx>(
impl
<
'cx
,
'tcx
>
dataflow
::
ResultsVisitor
<
'cx
,
'tcx
>
for
MirBorrowckCtxt
<
'cx
,
'tcx
>
{
type
FlowState
=
Flows
<
'cx
,
'tcx
>
;
fn
visit_statement
(
fn
visit_statement
_before_primary_effect
(
&
mut
self
,
flow_state
:
&
Flows
<
'cx
,
'tcx
>
,
stmt
:
&
'cx
Statement
<
'tcx
>
,
...
...
@@ -607,7 +607,7 @@ fn visit_statement(
}
}
fn
visit_terminator
(
fn
visit_terminator
_before_primary_effect
(
&
mut
self
,
flow_state
:
&
Flows
<
'cx
,
'tcx
>
,
term
:
&
'cx
Terminator
<
'tcx
>
,
...
...
@@ -701,7 +701,7 @@ fn visit_terminator(
}
}
fn
visit_terminator_
exi
t
(
fn
visit_terminator_
after_primary_effec
t
(
&
mut
self
,
flow_state
:
&
Flows
<
'cx
,
'tcx
>
,
term
:
&
'cx
Terminator
<
'tcx
>
,
...
...
src/librustc_mir/borrow_check/type_check/liveness/trace.rs
浏览文件 @
c68d710d
...
...
@@ -408,7 +408,7 @@ fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool {
/// DROP of some local variable will have an effect -- note that
/// drops, as they may unwind, are always terminators.
fn
initialized_at_terminator
(
&
mut
self
,
block
:
BasicBlock
,
mpi
:
MovePathIndex
)
->
bool
{
self
.flow_inits
.seek_before
(
self
.body
.terminator_loc
(
block
));
self
.flow_inits
.seek_before
_primary_effect
(
self
.body
.terminator_loc
(
block
));
self
.initialized_at_curr_loc
(
mpi
)
}
...
...
@@ -418,7 +418,7 @@ fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -
/// **Warning:** Does not account for the result of `Call`
/// instructions.
fn
initialized_at_exit
(
&
mut
self
,
block
:
BasicBlock
,
mpi
:
MovePathIndex
)
->
bool
{
self
.flow_inits
.seek_after
(
self
.body
.terminator_loc
(
block
));
self
.flow_inits
.seek_after
_primary_effect
(
self
.body
.terminator_loc
(
block
));
self
.initialized_at_curr_loc
(
mpi
)
}
...
...
src/librustc_mir/dataflow/framework/cursor.rs
浏览文件 @
c68d710d
//! Random access inspection of the results of a dataflow analysis.
use
std
::
borrow
::
Borrow
;
use
std
::
cmp
::
Ordering
;
use
rustc_index
::
bit_set
::
BitSet
;
use
rustc_middle
::
mir
::{
self
,
BasicBlock
,
Location
,
TerminatorKind
};
use
rustc_middle
::
mir
::{
self
,
BasicBlock
,
Location
};
use
super
::{
Analysis
,
Results
};
use
super
::{
Analysis
,
Direction
,
Effect
,
EffectIndex
,
Results
};
/// A `ResultsCursor` that borrows the underlying `Results`.
pub
type
ResultsRefCursor
<
'a
,
'mir
,
'tcx
,
A
>
=
ResultsCursor
<
'mir
,
'tcx
,
A
,
&
'a
Results
<
'tcx
,
A
>>
;
...
...
@@ -13,9 +14,9 @@
/// Allows random access inspection of the results of a dataflow analysis.
///
/// This cursor only has linear performance within a basic block when its statements are visited in
///
order. In the worst case—when statements are visited in *reverse* order—performance will b
e
///
quadratic in the number of statements in the block. The order in which basic blocks ar
e
/// inspected has no impact on performance.
///
the same order as the `DIRECTION` of the analysis. In the worst case—when statements ar
e
///
visited in *reverse* order—performance will be quadratic in the number of statements in th
e
///
block. The order in which basic blocks are
inspected has no impact on performance.
///
/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
...
...
@@ -29,14 +30,10 @@ pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
pos
:
CursorPosition
,
/// When this flag is set, the cursor is pointing at a `Call` or `Yield` terminator whose call
/// return or resume effect has been applied to `state`.
/// Indicates that `state` has been modified with a custom effect.
///
/// This flag helps to ensure that multiple calls to `seek_after_assume_success` with the
/// same target will result in exactly one invocation of `apply_call_return_effect`. It is
/// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
/// terminator will always require a cursor reset.
success_effect_applied
:
bool
,
/// When this flag is set, we need to reset to an entry set before doing a seek.
state_needs_reset
:
bool
,
}
impl
<
'mir
,
'tcx
,
A
,
R
>
ResultsCursor
<
'mir
,
'tcx
,
A
,
R
>
...
...
@@ -44,17 +41,21 @@ impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
A
:
Analysis
<
'tcx
>
,
R
:
Borrow
<
Results
<
'tcx
,
A
>>
,
{
/// Returns a new cursor for `results` that points to the
start
of the `START_BLOCK`.
/// Returns a new cursor for `results` that points to the
entry
of the `START_BLOCK`.
pub
fn
new
(
body
:
&
'mir
mir
::
Body
<
'tcx
>
,
results
:
R
)
->
Self
{
ResultsCursor
{
body
,
pos
:
CursorPosition
::
BlockStart
(
mir
::
START_BLOCK
),
state
:
results
.borrow
()
.entry_set
s
[
mir
::
START_BLOCK
]
.clone
(),
s
uccess_effect_applied
:
false
,
pos
:
CursorPosition
::
block_entry
(
mir
::
START_BLOCK
),
state
:
results
.borrow
()
.entry_set
_for_block
(
mir
::
START_BLOCK
)
.clone
(),
s
tate_needs_reset
:
false
,
results
,
}
}
pub
fn
body
(
&
self
)
->
&
'mir
mir
::
Body
<
'tcx
>
{
self
.body
}
/// Returns the `Analysis` used to generate the underlying results.
pub
fn
analysis
(
&
self
)
->
&
A
{
&
self
.results
.borrow
()
.analysis
...
...
@@ -72,209 +73,134 @@ pub fn contains(&self, elem: A::Idx) -> bool {
self
.state
.contains
(
elem
)
}
/// Resets the cursor to the start of the given basic block.
pub
fn
seek_to_block_start
(
&
mut
self
,
block
:
BasicBlock
)
{
self
.state
.overwrite
(
&
self
.results
.borrow
()
.entry_sets
[
block
]);
self
.pos
=
CursorPosition
::
BlockStart
(
block
);
self
.success_effect_applied
=
false
;
}
/// Advances the cursor to hold all effects up to and including to the "before" effect of the
/// statement (or terminator) at the given location.
/// Resets the cursor to hold the dataflow state for the given basic block at fixpoint.
///
/// If you wish to observe the full effect of a statement or terminator, not just the "before"
/// effect, use `seek_after` or `seek_after_assume_success`.
pub
fn
seek_before
(
&
mut
self
,
target
:
Location
)
{
assert
!
(
target
<=
self
.body
.terminator_loc
(
target
.block
));
self
.seek_
(
target
,
false
);
/// For forward dataflow analyses, this is the dataflow state prior to the first statement.
///
/// For backward dataflow analyses, this is the dataflow state after the terminator.
pub
(
super
)
fn
seek_to_block_entry
(
&
mut
self
,
block
:
BasicBlock
)
{
self
.state
.overwrite
(
&
self
.results
.borrow
()
.entry_set_for_block
(
block
));
self
.pos
=
CursorPosition
::
block_entry
(
block
);
self
.state_needs_reset
=
false
;
}
/// Advances the cursor to hold the full effect of all statements (and possibly closing
/// terminators) up to and including the `target`.
/// Resets the cursor to hold the state at the entry to the given block.
///
/// If the `target` is a `Call` terminator, any call return effect for that terminator will
/// **not** be observed. Use `seek_after_assume_success` if you wish to observe the call
/// return effect.
pub
fn
seek_after
(
&
mut
self
,
target
:
Location
)
{
assert
!
(
target
<=
self
.body
.terminator_loc
(
target
.block
));
// If we have already applied the call return effect, we are currently pointing at a `Call`
// terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
// the call return effect.
if
self
.success_effect_applied
{
self
.seek_to_block_start
(
target
.block
);
/// For forward analyses, this is the block's state at fixpoint.
///
/// For backward analyses, this is the state that will be propagated to its
/// predecessors (ignoring edge-specific effects).
pub
fn
seek_to_block_start
(
&
mut
self
,
block
:
BasicBlock
)
{
if
A
::
Direction
::
is_forward
()
{
self
.seek_to_block_entry
(
block
)
}
else
{
self
.seek_after
(
Location
{
block
,
statement_index
:
0
},
Effect
::
Primary
)
}
self
.seek_
(
target
,
true
);
}
/// Advances the cursor to hold all effects up to and including of the statement (or
/// terminator) at the given location.
/// Resets the cursor to hold the state at the exit of the given block.
///
/// If the `target` is a `Call` or `Yield` terminator, any call return or resume effect for that
/// terminator will be observed. Use `seek_after` if you do **not** wish to observe the
/// "success" effect.
pub
fn
seek_after_assume_success
(
&
mut
self
,
target
:
Location
)
{
let
terminator_loc
=
self
.body
.terminator_loc
(
target
.block
);
assert
!
(
target
.statement_index
<=
terminator_loc
.statement_index
);
self
.seek_
(
target
,
true
);
if
target
!=
terminator_loc
||
self
.success_effect_applied
{
return
;
}
// Apply the effect of the "success" path of the terminator.
self
.success_effect_applied
=
true
;
let
terminator
=
self
.body
.basic_blocks
()[
target
.block
]
.terminator
();
match
&
terminator
.kind
{
TerminatorKind
::
Call
{
destination
:
Some
((
return_place
,
_
)),
func
,
args
,
..
}
=>
{
self
.results
.borrow
()
.analysis
.apply_call_return_effect
(
&
mut
self
.state
,
target
.block
,
func
,
args
,
*
return_place
,
);
}
TerminatorKind
::
Yield
{
resume
,
resume_arg
,
..
}
=>
{
self
.results
.borrow
()
.analysis
.apply_yield_resume_effect
(
&
mut
self
.state
,
*
resume
,
*
resume_arg
,
);
}
_
=>
{}
/// For backward analyses, this is the block's state at fixpoint.
///
/// For forward analyses, this is the state that will be propagated to its
/// successors (ignoring edge-specific effects).
pub
fn
seek_to_block_end
(
&
mut
self
,
block
:
BasicBlock
)
{
if
A
::
Direction
::
is_backward
()
{
self
.seek_to_block_entry
(
block
)
}
else
{
self
.seek_after
(
self
.body
.terminator_loc
(
block
),
Effect
::
Primary
)
}
}
fn
seek_
(
&
mut
self
,
target
:
Location
,
apply_after_effect_at_target
:
bool
)
{
use
CursorPosition
::
*
;
match
self
.pos
{
// Return early if we are already at the target location.
Before
(
curr
)
if
curr
==
target
&&
!
apply_after_effect_at_target
=>
return
,
After
(
curr
)
if
curr
==
target
&&
apply_after_effect_at_target
=>
return
,
/// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is
/// applied.
///
/// The "before" effect at the target location *will be* applied.
pub
fn
seek_before_primary_effect
(
&
mut
self
,
target
:
Location
)
{
self
.seek_after
(
target
,
Effect
::
Before
)
}
// Otherwise, we must reset to the start of the target block if...
/// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is
/// applied.
///
/// The "before" effect at the target location will be applied as well.
pub
fn
seek_after_primary_effect
(
&
mut
self
,
target
:
Location
)
{
self
.seek_after
(
target
,
Effect
::
Primary
)
}
// we are in a different block entirely.
BlockStart
(
block
)
|
Before
(
Location
{
block
,
..
})
|
After
(
Location
{
block
,
..
})
if
block
!=
target
.block
=>
{
self
.seek_to_block_start
(
target
.block
)
}
fn
seek_after
(
&
mut
self
,
target
:
Location
,
effect
:
Effect
)
{
assert
!
(
target
<=
self
.body
.terminator_loc
(
target
.block
));
// we are in the same block but have advanced past the target statement.
Before
(
curr
)
|
After
(
curr
)
if
curr
.statement_index
>
target
.statement_index
=>
{
self
.seek_to_block_start
(
target
.block
)
// Reset to the entry of the target block if any of the following are true:
// - A custom effect has been applied to the cursor state.
// - We are in a different block than the target.
// - We are in the same block but have advanced past the target effect.
if
self
.state_needs_reset
||
self
.pos.block
!=
target
.block
{
self
.seek_to_block_entry
(
target
.block
);
}
else
if
let
Some
(
curr_effect
)
=
self
.pos.curr_effect_index
{
let
mut
ord
=
curr_effect
.statement_index
.cmp
(
&
target
.statement_index
);
if
A
::
Direction
::
is_backward
()
{
ord
=
ord
.reverse
()
}
// we have already applied the entire effect of a statement but only wish to observe
// its "before" effect.
After
(
curr
)
if
curr
.statement_index
==
target
.statement_index
&&
!
apply_after_effect_at_target
=>
{
self
.seek_to_block_start
(
target
.block
)
match
ord
.then_with
(||
curr_effect
.effect
.cmp
(
&
effect
))
{
Ordering
::
Equal
=>
return
,
Ordering
::
Greater
=>
self
.seek_to_block_entry
(
target
.block
),
Ordering
::
Less
=>
{}
}
// N.B., `success_effect_applied` is checked in `seek_after`, not here.
_
=>
(),
}
let
analysis
=
&
self
.results
.borrow
()
.analysis
;
let
block_data
=
&
self
.body
.basic_blocks
()[
target
.block
];
// At this point, the cursor is in the same block as the target location at an earlier
// statement.
debug_assert_eq!
(
target
.block
,
self
.pos
.block
());
// Find the first statement whose transfer function has not yet been applied.
let
first_unapplied_statement
=
match
self
.pos
{
BlockStart
(
_
)
=>
0
,
After
(
Location
{
statement_index
,
..
})
=>
statement_index
+
1
,
// If we have only applied the "before" effect for the current statement, apply the
// remainder before continuing.
Before
(
curr
)
=>
{
if
curr
.statement_index
==
block_data
.statements
.len
()
{
let
terminator
=
block_data
.terminator
();
analysis
.apply_terminator_effect
(
&
mut
self
.state
,
terminator
,
curr
);
}
else
{
let
statement
=
&
block_data
.statements
[
curr
.statement_index
];
analysis
.apply_statement_effect
(
&
mut
self
.state
,
statement
,
curr
);
}
// If all we needed to do was go from `Before` to `After` in the same statement,
// we are now done.
if
curr
.statement_index
==
target
.statement_index
{
debug_assert!
(
apply_after_effect_at_target
);
self
.pos
=
After
(
target
);
return
;
}
curr
.statement_index
+
1
}
debug_assert_eq!
(
target
.block
,
self
.pos.block
);
let
block_data
=
&
self
.body
[
target
.block
];
let
next_effect
=
if
A
::
Direction
::
is_forward
()
{
#[rustfmt::skip]
self
.pos.curr_effect_index
.map_or_else
(
||
Effect
::
Before
.at_index
(
0
),
EffectIndex
::
next_in_forward_order
,
)
}
else
{
self
.pos.curr_effect_index
.map_or_else
(
||
Effect
::
Before
.at_index
(
block_data
.statements
.len
()),
EffectIndex
::
next_in_backward_order
,
)
};
// We have now applied all effects prior to `first_unapplied_statement`.
// Apply the effects of all statements before `target`.
let
mut
location
=
Location
{
block
:
target
.block
,
statement_index
:
0
};
for
statement_index
in
first_unapplied_statement
..
target
.statement_index
{
location
.statement_index
=
statement_index
;
let
statement
=
&
block_data
.statements
[
statement_index
];
analysis
.apply_before_statement_effect
(
&
mut
self
.state
,
statement
,
location
);
analysis
.apply_statement_effect
(
&
mut
self
.state
,
statement
,
location
);
}
// Apply the effect of the statement (or terminator) at `target`.
location
.statement_index
=
target
.statement_index
;
if
target
.statement_index
==
block_data
.statements
.len
()
{
let
terminator
=
&
block_data
.terminator
();
analysis
.apply_before_terminator_effect
(
&
mut
self
.state
,
terminator
,
location
);
if
apply_after_effect_at_target
{
analysis
.apply_terminator_effect
(
&
mut
self
.state
,
terminator
,
location
);
self
.pos
=
After
(
target
);
}
else
{
self
.pos
=
Before
(
target
);
}
}
else
{
let
statement
=
&
block_data
.statements
[
target
.statement_index
];
analysis
.apply_before_statement_effect
(
&
mut
self
.state
,
statement
,
location
);
let
analysis
=
&
self
.results
.borrow
()
.analysis
;
let
target_effect_index
=
effect
.at_index
(
target
.statement_index
);
A
::
Direction
::
apply_effects_in_range
(
analysis
,
&
mut
self
.state
,
target
.block
,
block_data
,
next_effect
..=
target_effect_index
,
);
self
.pos
=
CursorPosition
{
block
:
target
.block
,
curr_effect_index
:
Some
(
target_effect_index
)
};
}
if
apply_after_effect_at_target
{
analysis
.apply_statement_effect
(
&
mut
self
.state
,
statement
,
location
);
self
.pos
=
After
(
target
)
}
else
{
self
.pos
=
Before
(
target
);
}
}
/// Applies `f` to the cursor's internal state.
///
/// This can be used, e.g., to apply the call return effect directly to the cursor without
/// creating an extra copy of the dataflow state.
pub
fn
apply_custom_effect
(
&
mut
self
,
f
:
impl
FnOnce
(
&
A
,
&
mut
BitSet
<
A
::
Idx
>
))
{
f
(
&
self
.results
.borrow
()
.analysis
,
&
mut
self
.state
);
self
.state_needs_reset
=
true
;
}
}
#[derive(Clone,
Copy,
Debug)]
enum
CursorPosition
{
/// No effects within this block have been applied.
BlockStart
(
BasicBlock
),
/// Only the "before" effect of the statement (or terminator) at this location has been
/// applied (along with the effects of all previous statements).
Before
(
Location
),
/// The effects of all statements up to and including the one at this location have been
/// applied.
After
(
Location
),
struct
CursorPosition
{
block
:
BasicBlock
,
curr_effect_index
:
Option
<
EffectIndex
>
,
}
impl
CursorPosition
{
fn
block
(
&
self
)
->
BasicBlock
{
match
*
self
{
Self
::
BlockStart
(
block
)
=>
block
,
Self
::
Before
(
loc
)
|
Self
::
After
(
loc
)
=>
loc
.block
,
}
fn
block_entry
(
block
:
BasicBlock
)
->
CursorPosition
{
CursorPosition
{
block
,
curr_effect_index
:
None
}
}
}
src/librustc_mir/dataflow/framework/direction.rs
0 → 100644
浏览文件 @
c68d710d
此差异已折叠。
点击以展开。
src/librustc_mir/dataflow/framework/engine.rs
浏览文件 @
c68d710d
...
...
@@ -9,14 +9,58 @@
use
rustc_hir
::
def_id
::
DefId
;
use
rustc_index
::
bit_set
::
BitSet
;
use
rustc_index
::
vec
::
IndexVec
;
use
rustc_middle
::
mir
::{
self
,
traversal
,
BasicBlock
,
Location
};
use
rustc_middle
::
mir
::{
self
,
traversal
,
BasicBlock
};
use
rustc_middle
::
ty
::{
self
,
TyCtxt
};
use
rustc_span
::
symbol
::{
sym
,
Symbol
};
use
super
::
graphviz
;
use
super
::{
Analysis
,
GenKillAnalysis
,
GenKillSet
,
Results
};
use
super
::{
visit_results
,
Analysis
,
Direction
,
GenKillAnalysis
,
GenKillSet
,
ResultsCursor
,
ResultsVisitor
,
};
use
crate
::
util
::
pretty
::
dump_enabled
;
/// A dataflow analysis that has converged to fixpoint.
pub
struct
Results
<
'tcx
,
A
>
where
A
:
Analysis
<
'tcx
>
,
{
pub
analysis
:
A
,
pub
(
super
)
entry_sets
:
IndexVec
<
BasicBlock
,
BitSet
<
A
::
Idx
>>
,
}
impl
<
A
>
Results
<
'tcx
,
A
>
where
A
:
Analysis
<
'tcx
>
,
{
/// Creates a `ResultsCursor` that can inspect these `Results`.
pub
fn
into_results_cursor
(
self
,
body
:
&
'mir
mir
::
Body
<
'tcx
>
)
->
ResultsCursor
<
'mir
,
'tcx
,
A
>
{
ResultsCursor
::
new
(
body
,
self
)
}
/// Gets the dataflow state for the given block.
pub
fn
entry_set_for_block
(
&
self
,
block
:
BasicBlock
)
->
&
BitSet
<
A
::
Idx
>
{
&
self
.entry_sets
[
block
]
}
pub
fn
visit_with
(
&
self
,
body
:
&
'mir
mir
::
Body
<
'tcx
>
,
blocks
:
impl
IntoIterator
<
Item
=
BasicBlock
>
,
vis
:
&
mut
impl
ResultsVisitor
<
'mir
,
'tcx
,
FlowState
=
BitSet
<
A
::
Idx
>>
,
)
{
visit_results
(
body
,
blocks
,
self
,
vis
)
}
pub
fn
visit_in_rpo_with
(
&
self
,
body
:
&
'mir
mir
::
Body
<
'tcx
>
,
vis
:
&
mut
impl
ResultsVisitor
<
'mir
,
'tcx
,
FlowState
=
BitSet
<
A
::
Idx
>>
,
)
{
let
blocks
=
mir
::
traversal
::
reverse_postorder
(
body
);
visit_results
(
body
,
blocks
.map
(|(
bb
,
_
)|
bb
),
self
,
vis
)
}
}
/// A solver for dataflow problems.
pub
struct
Engine
<
'a
,
'tcx
,
A
>
where
...
...
@@ -61,17 +105,7 @@ pub fn new_gen_kill(
for
(
block
,
block_data
)
in
body
.basic_blocks
()
.iter_enumerated
()
{
let
trans
=
&
mut
trans_for_block
[
block
];
for
(
i
,
statement
)
in
block_data
.statements
.iter
()
.enumerate
()
{
let
loc
=
Location
{
block
,
statement_index
:
i
};
analysis
.before_statement_effect
(
trans
,
statement
,
loc
);
analysis
.statement_effect
(
trans
,
statement
,
loc
);
}
let
terminator
=
block_data
.terminator
();
let
loc
=
Location
{
block
,
statement_index
:
block_data
.statements
.len
()
};
analysis
.before_terminator_effect
(
trans
,
terminator
,
loc
);
analysis
.terminator_effect
(
trans
,
terminator
,
loc
);
A
::
Direction
::
gen_kill_effects_in_block
(
&
analysis
,
trans
,
block
,
block_data
);
}
Self
::
new
(
tcx
,
body
,
def_id
,
analysis
,
Some
(
trans_for_block
))
...
...
@@ -111,9 +145,13 @@ fn new(
BitSet
::
new_empty
(
bits_per_block
)
};
let
mut
entry_sets
=
IndexVec
::
from_elem
(
bottom_value_set
,
body
.basic_blocks
());
let
mut
entry_sets
=
IndexVec
::
from_elem
(
bottom_value_set
.clone
()
,
body
.basic_blocks
());
analysis
.initialize_start_block
(
body
,
&
mut
entry_sets
[
mir
::
START_BLOCK
]);
if
A
::
Direction
::
is_backward
()
&&
entry_sets
[
mir
::
START_BLOCK
]
!=
bottom_value_set
{
bug!
(
"`initialize_start_block` is not yet supported for backward dataflow analyses"
);
}
Engine
{
analysis
,
bits_per_block
,
...
...
@@ -137,251 +175,79 @@ pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
}
/// Computes the fixpoint for this dataflow problem and returns it.
pub
fn
iterate_to_fixpoint
(
mut
self
)
->
Results
<
'tcx
,
A
>
{
let
mut
temp_state
=
BitSet
::
new_empty
(
self
.bits_per_block
);
pub
fn
iterate_to_fixpoint
(
self
)
->
Results
<
'tcx
,
A
>
{
let
Engine
{
analysis
,
bits_per_block
,
body
,
dead_unwinds
,
def_id
,
mut
entry_sets
,
tcx
,
trans_for_block
,
..
}
=
self
;
let
mut
dirty_queue
:
WorkQueue
<
BasicBlock
>
=
WorkQueue
::
with_none
(
self
.
body
.basic_blocks
()
.len
());
WorkQueue
::
with_none
(
body
.basic_blocks
()
.len
());
for
(
bb
,
_
)
in
traversal
::
reverse_postorder
(
self
.body
)
{
dirty_queue
.insert
(
bb
);
if
A
::
Direction
::
is_forward
()
{
for
(
bb
,
_
)
in
traversal
::
reverse_postorder
(
body
)
{
dirty_queue
.insert
(
bb
);
}
}
else
{
// Reverse post-order on the reverse CFG may generate a better iteration order for
// backward dataflow analyses, but probably not enough to matter.
for
(
bb
,
_
)
in
traversal
::
postorder
(
body
)
{
dirty_queue
.insert
(
bb
);
}
}
// Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will
// be processed after the ones added above.
for
bb
in
self
.body
.basic_blocks
()
.indices
()
{
//
// FIXME(ecstaticmorse): Is this actually necessary? In principle, we shouldn't need to
// know the dataflow state in unreachable basic blocks.
for
bb
in
body
.basic_blocks
()
.indices
()
{
dirty_queue
.insert
(
bb
);
}
let
mut
state
=
BitSet
::
new_empty
(
bits_per_block
);
while
let
Some
(
bb
)
=
dirty_queue
.pop
()
{
let
bb_data
=
&
self
.body
[
bb
];
let
on_entry
=
&
self
.entry_sets
[
bb
];
let
bb_data
=
&
body
[
bb
];
temp_state
.overwrite
(
on_entry
);
self
.apply_whole_block_effect
(
&
mut
temp_state
,
bb
,
bb_data
);
// Apply the block transfer function, using the cached one if it exists.
state
.overwrite
(
&
entry_sets
[
bb
]);
match
&
trans_for_block
{
Some
(
trans_for_block
)
=>
trans_for_block
[
bb
]
.apply
(
&
mut
state
),
None
=>
A
::
Direction
::
apply_effects_in_block
(
&
analysis
,
&
mut
state
,
bb
,
bb_data
),
}
self
.propagate_bits_into_graph_successors_of
(
&
mut
temp_state
,
A
::
Direction
::
join_state_into_successors_of
(
&
analysis
,
tcx
,
body
,
dead_unwinds
,
&
mut
state
,
(
bb
,
bb_data
),
&
mut
dirty_queue
,
|
target
:
BasicBlock
,
state
:
&
BitSet
<
A
::
Idx
>
|
{
let
set_changed
=
analysis
.join
(
&
mut
entry_sets
[
target
],
state
);
if
set_changed
{
dirty_queue
.insert
(
target
);
}
},
);
}
let
Engine
{
tcx
,
body
,
def_id
,
trans_for_block
,
entry_sets
,
analysis
,
..
}
=
self
;
let
results
=
Results
{
analysis
,
entry_sets
};
let
res
=
write_graphviz_results
(
tcx
,
def_id
,
body
,
&
results
,
trans_for_block
);
let
res
=
write_graphviz_results
(
tcx
,
def_id
,
&
body
,
&
results
,
trans_for_block
);
if
let
Err
(
e
)
=
res
{
warn!
(
"Failed to write graphviz dataflow results: {}"
,
e
);
}
results
}
/// Applies the cumulative effect of an entire block, excluding the call return effect if one
/// exists.
fn
apply_whole_block_effect
(
&
self
,
state
:
&
mut
BitSet
<
A
::
Idx
>
,
block
:
BasicBlock
,
block_data
:
&
mir
::
BasicBlockData
<
'tcx
>
,
)
{
// Use the cached block transfer function if available.
if
let
Some
(
trans_for_block
)
=
&
self
.trans_for_block
{
trans_for_block
[
block
]
.apply
(
state
);
return
;
}
// Otherwise apply effects one-by-one.
for
(
statement_index
,
statement
)
in
block_data
.statements
.iter
()
.enumerate
()
{
let
location
=
Location
{
block
,
statement_index
};
self
.analysis
.apply_before_statement_effect
(
state
,
statement
,
location
);
self
.analysis
.apply_statement_effect
(
state
,
statement
,
location
);
}
let
terminator
=
block_data
.terminator
();
let
location
=
Location
{
block
,
statement_index
:
block_data
.statements
.len
()
};
self
.analysis
.apply_before_terminator_effect
(
state
,
terminator
,
location
);
self
.analysis
.apply_terminator_effect
(
state
,
terminator
,
location
);
}
fn
propagate_bits_into_graph_successors_of
(
&
mut
self
,
in_out
:
&
mut
BitSet
<
A
::
Idx
>
,
(
bb
,
bb_data
):
(
BasicBlock
,
&
'a
mir
::
BasicBlockData
<
'tcx
>
),
dirty_list
:
&
mut
WorkQueue
<
BasicBlock
>
,
)
{
use
mir
::
TerminatorKind
::
*
;
match
bb_data
.terminator
()
.kind
{
Return
|
Resume
|
Abort
|
GeneratorDrop
|
Unreachable
=>
{}
Goto
{
target
}
|
Assert
{
target
,
cleanup
:
None
,
..
}
|
Drop
{
target
,
location
:
_
,
unwind
:
None
}
|
DropAndReplace
{
target
,
value
:
_
,
location
:
_
,
unwind
:
None
}
=>
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
target
,
dirty_list
)
}
Yield
{
resume
:
target
,
drop
,
resume_arg
,
..
}
=>
{
if
let
Some
(
drop
)
=
drop
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
drop
,
dirty_list
);
}
self
.analysis
.apply_yield_resume_effect
(
in_out
,
target
,
resume_arg
);
self
.propagate_bits_into_entry_set_for
(
in_out
,
target
,
dirty_list
);
}
Assert
{
target
,
cleanup
:
Some
(
unwind
),
..
}
|
Drop
{
target
,
location
:
_
,
unwind
:
Some
(
unwind
)
}
|
DropAndReplace
{
target
,
value
:
_
,
location
:
_
,
unwind
:
Some
(
unwind
)
}
=>
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
target
,
dirty_list
);
if
self
.dead_unwinds
.map_or
(
true
,
|
bbs
|
!
bbs
.contains
(
bb
))
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
unwind
,
dirty_list
);
}
}
SwitchInt
{
ref
targets
,
ref
values
,
ref
discr
,
..
}
=>
{
let
Engine
{
tcx
,
body
,
..
}
=
*
self
;
let
enum_
=
discr
.place
()
.and_then
(|
discr
|
switch_on_enum_discriminant
(
tcx
,
body
,
bb_data
,
discr
));
match
enum_
{
// If this is a switch on an enum discriminant, a custom effect may be applied
// along each outgoing edge.
Some
((
enum_place
,
enum_def
))
=>
{
self
.propagate_bits_into_enum_discriminant_switch_successors
(
in_out
,
bb
,
enum_def
,
enum_place
,
dirty_list
,
&*
values
,
&*
targets
,
);
}
// Otherwise, it's just a normal `SwitchInt`, and every successor sees the same
// exit state.
None
=>
{
for
target
in
targets
.iter
()
.copied
()
{
self
.propagate_bits_into_entry_set_for
(
&
in_out
,
target
,
dirty_list
);
}
}
}
}
Call
{
cleanup
,
ref
destination
,
ref
func
,
ref
args
,
..
}
=>
{
if
let
Some
(
unwind
)
=
cleanup
{
if
self
.dead_unwinds
.map_or
(
true
,
|
bbs
|
!
bbs
.contains
(
bb
))
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
unwind
,
dirty_list
);
}
}
if
let
Some
((
dest_place
,
dest_bb
))
=
*
destination
{
// N.B.: This must be done *last*, otherwise the unwind path will see the call
// return effect.
self
.analysis
.apply_call_return_effect
(
in_out
,
bb
,
func
,
args
,
dest_place
);
self
.propagate_bits_into_entry_set_for
(
in_out
,
dest_bb
,
dirty_list
);
}
}
FalseEdges
{
real_target
,
imaginary_target
}
=>
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
real_target
,
dirty_list
);
self
.propagate_bits_into_entry_set_for
(
in_out
,
imaginary_target
,
dirty_list
);
}
FalseUnwind
{
real_target
,
unwind
}
=>
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
real_target
,
dirty_list
);
if
let
Some
(
unwind
)
=
unwind
{
if
self
.dead_unwinds
.map_or
(
true
,
|
bbs
|
!
bbs
.contains
(
bb
))
{
self
.propagate_bits_into_entry_set_for
(
in_out
,
unwind
,
dirty_list
);
}
}
}
}
}
fn
propagate_bits_into_entry_set_for
(
&
mut
self
,
in_out
:
&
BitSet
<
A
::
Idx
>
,
bb
:
BasicBlock
,
dirty_queue
:
&
mut
WorkQueue
<
BasicBlock
>
,
)
{
let
entry_set
=
&
mut
self
.entry_sets
[
bb
];
let
set_changed
=
self
.analysis
.join
(
entry_set
,
&
in_out
);
if
set_changed
{
dirty_queue
.insert
(
bb
);
}
}
fn
propagate_bits_into_enum_discriminant_switch_successors
(
&
mut
self
,
in_out
:
&
mut
BitSet
<
A
::
Idx
>
,
bb
:
BasicBlock
,
enum_def
:
&
'tcx
ty
::
AdtDef
,
enum_place
:
mir
::
Place
<
'tcx
>
,
dirty_list
:
&
mut
WorkQueue
<
BasicBlock
>
,
values
:
&
[
u128
],
targets
:
&
[
BasicBlock
],
)
{
// MIR building adds discriminants to the `values` array in the same order as they
// are yielded by `AdtDef::discriminants`. We rely on this to match each
// discriminant in `values` to its corresponding variant in linear time.
let
mut
tmp
=
BitSet
::
new_empty
(
in_out
.domain_size
());
let
mut
discriminants
=
enum_def
.discriminants
(
self
.tcx
);
for
(
value
,
target
)
in
values
.iter
()
.zip
(
targets
.iter
()
.copied
())
{
let
(
variant_idx
,
_
)
=
discriminants
.find
(|
&
(
_
,
discr
)|
discr
.val
==
*
value
)
.expect
(
"Order of `AdtDef::discriminants` differed from that of `SwitchInt::values`"
,
);
tmp
.overwrite
(
in_out
);
self
.analysis
.apply_discriminant_switch_effect
(
&
mut
tmp
,
bb
,
enum_place
,
enum_def
,
variant_idx
,
);
self
.propagate_bits_into_entry_set_for
(
&
tmp
,
target
,
dirty_list
);
}
std
::
mem
::
drop
(
tmp
);
// Propagate dataflow state along the "otherwise" edge.
let
otherwise
=
targets
.last
()
.copied
()
.unwrap
();
self
.propagate_bits_into_entry_set_for
(
&
in_out
,
otherwise
,
dirty_list
);
}
}
/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is
/// an enum discriminant.
///
/// We expect such blocks to have a call to `discriminant` as their last statement like so:
/// _42 = discriminant(_1)
/// SwitchInt(_42, ..)
///
/// If the basic block matches this pattern, this function returns the place corresponding to the
/// enum (`_1` in the example above) as well as the `AdtDef` of that enum.
fn
switch_on_enum_discriminant
(
tcx
:
TyCtxt
<
'tcx
>
,
body
:
&
'mir
mir
::
Body
<
'tcx
>
,
block
:
&
'mir
mir
::
BasicBlockData
<
'tcx
>
,
switch_on
:
mir
::
Place
<
'tcx
>
,
)
->
Option
<
(
mir
::
Place
<
'tcx
>
,
&
'tcx
ty
::
AdtDef
)
>
{
match
block
.statements
.last
()
.map
(|
stmt
|
&
stmt
.kind
)
{
Some
(
mir
::
StatementKind
::
Assign
(
box
(
lhs
,
mir
::
Rvalue
::
Discriminant
(
discriminated
))))
if
*
lhs
==
switch_on
=>
{
match
&
discriminated
.ty
(
body
,
tcx
)
.ty.kind
{
ty
::
Adt
(
def
,
_
)
=>
Some
((
*
discriminated
,
def
)),
// `Rvalue::Discriminant` is also used to get the active yield point for a
// generator, but we do not need edge-specific effects in that case. This may
// change in the future.
ty
::
Generator
(
..
)
=>
None
,
t
=>
bug!
(
"`discriminant` called on unexpected type {:?}"
,
t
),
}
}
_
=>
None
,
}
}
// Graphviz
...
...
@@ -431,12 +297,12 @@ fn write_graphviz_results<A>(
if
let
Some
(
trans_for_block
)
=
block_transfer_functions
{
Box
::
new
(
graphviz
::
BlockTransferFunc
::
new
(
body
,
trans_for_block
))
}
else
{
Box
::
new
(
graphviz
::
SimpleDiff
::
new
(
b
its_per_block
))
Box
::
new
(
graphviz
::
SimpleDiff
::
new
(
b
ody
,
&
results
))
}
}
// Default to the `SimpleDiff` output style.
_
=>
Box
::
new
(
graphviz
::
SimpleDiff
::
new
(
b
its_per_block
)),
_
=>
Box
::
new
(
graphviz
::
SimpleDiff
::
new
(
b
ody
,
&
results
)),
};
debug!
(
"printing dataflow results for {:?} to {}"
,
def_id
,
path
.display
());
...
...
src/librustc_mir/dataflow/framework/graphviz.rs
浏览文件 @
c68d710d
...
...
@@ -8,7 +8,7 @@
use
rustc_index
::
vec
::{
Idx
,
IndexVec
};
use
rustc_middle
::
mir
::{
self
,
BasicBlock
,
Body
,
Location
};
use
super
::{
Analysis
,
GenKillSet
,
Results
,
ResultsRefCursor
};
use
super
::{
Analysis
,
Direction
,
GenKillSet
,
Results
,
ResultsRefCursor
};
use
crate
::
util
::
graphviz_safe_def_name
;
pub
struct
Formatter
<
'a
,
'tcx
,
A
>
...
...
@@ -49,7 +49,7 @@ pub struct CfgEdge {
index
:
usize
,
}
fn
outgoing_edges
(
body
:
&
Body
<
'_
>
,
bb
:
BasicBlock
)
->
Vec
<
CfgEdge
>
{
fn
dataflow_successors
(
body
:
&
Body
<
'tcx
>
,
bb
:
BasicBlock
)
->
Vec
<
CfgEdge
>
{
body
[
bb
]
.terminator
()
.successors
()
...
...
@@ -105,7 +105,7 @@ fn edges(&self) -> dot::Edges<'_, Self::Edge> {
self
.body
.basic_blocks
()
.indices
()
.flat_map
(|
bb
|
outgoing_edge
s
(
self
.body
,
bb
))
.flat_map
(|
bb
|
dataflow_successor
s
(
self
.body
,
bb
))
.collect
::
<
Vec
<
_
>>
()
.into
()
}
...
...
@@ -192,12 +192,12 @@ fn write_node_label(
self
.write_block_header_with_state_columns
(
w
,
block
)
?
;
}
// C:
Entry state
// C:
State at start of block
self
.bg
=
Background
::
Light
;
self
.results
.seek_to_block_start
(
block
);
let
block_entry_state
=
self
.results
.get
()
.clone
();
self
.write_row_with_full_state
(
w
,
""
,
"(on
entry
)"
)
?
;
self
.write_row_with_full_state
(
w
,
""
,
"(on
start
)"
)
?
;
// D: Statement transfer functions
for
(
i
,
statement
)
in
body
[
block
]
.statements
.iter
()
.enumerate
()
{
...
...
@@ -214,37 +214,72 @@ fn write_node_label(
self
.write_row_for_location
(
w
,
"T"
,
&
terminator_str
,
terminator_loc
)
?
;
// F:
Exit state
// F:
State at end of block
// Write the full dataflow state immediately after the terminator if it differs from the
// state at block entry.
self
.results
.seek_
after
(
terminator_loc
);
if
self
.results
.get
()
!=
&
block_entry_state
{
self
.results
.seek_
to_block_end
(
block
);
if
self
.results
.get
()
!=
&
block_entry_state
||
A
::
Direction
::
is_backward
()
{
let
after_terminator_name
=
match
terminator
.kind
{
mir
::
TerminatorKind
::
Call
{
destination
:
Some
(
_
),
..
}
=>
"(on unwind)"
,
_
=>
"(on e
xit
)"
,
_
=>
"(on e
nd
)"
,
};
self
.write_row_with_full_state
(
w
,
""
,
after_terminator_name
)
?
;
}
// Write any changes caused by terminator-specific effects
if
let
mir
::
TerminatorKind
::
Call
{
destination
:
Some
(
_
),
..
}
=
terminator
.kind
{
let
num_state_columns
=
self
.num_state_columns
();
self
.write_row
(
w
,
""
,
"(on successful return)"
,
|
this
,
w
,
fmt
|
{
write!
(
w
,
r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#
,
colspan
=
num_state_columns
,
fmt
=
fmt
,
)
?
;
let
state_on_unwind
=
this
.results
.get
()
.clone
();
this
.results
.seek_after_assume_success
(
terminator_loc
);
write_diff
(
w
,
this
.results
.analysis
(),
&
state_on_unwind
,
this
.results
.get
())
?
;
write!
(
w
,
"</td>"
)
})
?
;
let
num_state_columns
=
self
.num_state_columns
();
match
terminator
.kind
{
mir
::
TerminatorKind
::
Call
{
destination
:
Some
((
return_place
,
_
)),
ref
func
,
ref
args
,
..
}
=>
{
self
.write_row
(
w
,
""
,
"(on successful return)"
,
|
this
,
w
,
fmt
|
{
write!
(
w
,
r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#
,
colspan
=
num_state_columns
,
fmt
=
fmt
,
)
?
;
let
state_on_unwind
=
this
.results
.get
()
.clone
();
this
.results
.apply_custom_effect
(|
analysis
,
state
|
{
analysis
.apply_call_return_effect
(
state
,
block
,
func
,
args
,
return_place
);
});
write_diff
(
w
,
this
.results
.analysis
(),
&
state_on_unwind
,
this
.results
.get
())
?
;
write!
(
w
,
"</td>"
)
})
?
;
}
mir
::
TerminatorKind
::
Yield
{
resume
,
resume_arg
,
..
}
=>
{
self
.write_row
(
w
,
""
,
"(on yield resume)"
,
|
this
,
w
,
fmt
|
{
write!
(
w
,
r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#
,
colspan
=
num_state_columns
,
fmt
=
fmt
,
)
?
;
let
state_on_generator_drop
=
this
.results
.get
()
.clone
();
this
.results
.apply_custom_effect
(|
analysis
,
state
|
{
analysis
.apply_yield_resume_effect
(
state
,
resume
,
resume_arg
);
});
write_diff
(
w
,
this
.results
.analysis
(),
&
state_on_generator_drop
,
this
.results
.get
(),
)
?
;
write!
(
w
,
"</td>"
)
})
?
;
}
_
=>
{}
};
write!
(
w
,
"</table>"
)
...
...
@@ -403,18 +438,23 @@ fn write_state_for_location(
}
/// Prints a single column containing the state vector immediately *after* each statement.
pub
struct
SimpleDiff
<
T
:
Idx
>
{
prev_state
:
BitSet
<
T
>
,
prev_loc
:
Location
,
pub
struct
SimpleDiff
<
'a
,
'tcx
,
A
>
where
A
:
Analysis
<
'tcx
>
,
{
prev_state
:
ResultsRefCursor
<
'a
,
'a
,
'tcx
,
A
>
,
}
impl
<
T
:
Idx
>
SimpleDiff
<
T
>
{
pub
fn
new
(
bits_per_block
:
usize
)
->
Self
{
SimpleDiff
{
prev_state
:
BitSet
::
new_empty
(
bits_per_block
),
prev_loc
:
Location
::
START
}
impl
<
A
>
SimpleDiff
<
'a
,
'tcx
,
A
>
where
A
:
Analysis
<
'tcx
>
,
{
pub
fn
new
(
body
:
&
'a
Body
<
'tcx
>
,
results
:
&
'a
Results
<
'tcx
,
A
>
)
->
Self
{
SimpleDiff
{
prev_state
:
ResultsRefCursor
::
new
(
body
,
results
)
}
}
}
impl
<
A
>
StateFormatter
<
'tcx
,
A
>
for
SimpleDiff
<
A
::
Idx
>
impl
<
A
>
StateFormatter
<
'tcx
,
A
>
for
SimpleDiff
<
'_
,
'tcx
,
A
>
where
A
:
Analysis
<
'tcx
>
,
{
...
...
@@ -429,20 +469,27 @@ fn write_state_for_location(
results
:
&
mut
ResultsRefCursor
<
'_
,
'_
,
'tcx
,
A
>
,
location
:
Location
,
)
->
io
::
Result
<
()
>
{
if
location
.statement_index
==
0
{
results
.seek_to_block_start
(
location
.block
);
self
.prev_state
.overwrite
(
results
.get
());
if
A
::
Direction
::
is_forward
()
{
if
location
.statement_index
==
0
{
self
.prev_state
.seek_to_block_start
(
location
.block
);
}
else
{
self
.prev_state
.seek_after_primary_effect
(
Location
{
statement_index
:
location
.statement_index
-
1
,
..
location
});
}
}
else
{
// Ensure that we are visiting statements in order, so `prev_state` is correct.
assert_eq!
(
self
.prev_loc
.successor_within_block
(),
location
);
if
location
==
results
.body
()
.terminator_loc
(
location
.block
)
{
self
.prev_state
.seek_to_block_end
(
location
.block
);
}
else
{
self
.prev_state
.seek_after_primary_effect
(
location
.successor_within_block
());
}
}
self
.prev_loc
=
location
;
write!
(
w
,
r#"<td {fmt} balign="left" align="left">"#
,
fmt
=
fmt
)
?
;
results
.seek_after
(
location
);
results
.seek_after
_primary_effect
(
location
);
let
curr_state
=
results
.get
();
write_diff
(
&
mut
w
,
results
.analysis
(),
&
self
.prev_state
,
curr_state
)
?
;
self
.prev_state
.overwrite
(
curr_state
);
write_diff
(
&
mut
w
,
results
.analysis
(),
self
.prev_state
.get
(),
curr_state
)
?
;
write!
(
w
,
"</td>"
)
}
}
...
...
@@ -476,7 +523,7 @@ fn write_state_for_location(
location
:
Location
,
)
->
io
::
Result
<
()
>
{
if
location
.statement_index
==
0
{
results
.seek_to_block_
start
(
location
.block
);
results
.seek_to_block_
entry
(
location
.block
);
self
.prev_state
.overwrite
(
results
.get
());
}
else
{
// Ensure that we are visiting statements in order, so `prev_state` is correct.
...
...
@@ -488,7 +535,7 @@ fn write_state_for_location(
// Before
write!
(
w
,
r#"<td {fmt} align="left">"#
,
fmt
=
fmt
)
?
;
results
.seek_before
(
location
);
results
.seek_before
_primary_effect
(
location
);
let
curr_state
=
results
.get
();
write_diff
(
&
mut
w
,
results
.analysis
(),
&
self
.prev_state
,
curr_state
)
?
;
self
.prev_state
.overwrite
(
curr_state
);
...
...
@@ -497,7 +544,7 @@ fn write_state_for_location(
// After
write!
(
w
,
r#"<td {fmt} align="left">"#
,
fmt
=
fmt
)
?
;
results
.seek_after
(
location
);
results
.seek_after
_primary_effect
(
location
);
let
curr_state
=
results
.get
();
write_diff
(
&
mut
w
,
results
.analysis
(),
&
self
.prev_state
,
curr_state
)
?
;
self
.prev_state
.overwrite
(
curr_state
);
...
...
src/librustc_mir/dataflow/framework/mod.rs
浏览文件 @
c68d710d
...
...
@@ -30,67 +30,28 @@
//!
//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
use
std
::
cmp
::
Ordering
;
use
std
::
io
;
use
rustc_hir
::
def_id
::
DefId
;
use
rustc_index
::
bit_set
::{
BitSet
,
HybridBitSet
};
use
rustc_index
::
vec
::
{
Idx
,
IndexVec
}
;
use
rustc_index
::
vec
::
Idx
;
use
rustc_middle
::
mir
::{
self
,
BasicBlock
,
Location
};
use
rustc_middle
::
ty
::{
self
,
TyCtxt
};
use
rustc_target
::
abi
::
VariantIdx
;
mod
cursor
;
mod
direction
;
mod
engine
;
mod
graphviz
;
mod
visitor
;
pub
use
self
::
cursor
::{
ResultsCursor
,
ResultsRefCursor
};
pub
use
self
::
engine
::
Engine
;
pub
use
self
::
direction
::{
Backward
,
Direction
,
Forward
};
pub
use
self
::
engine
::{
Engine
,
Results
};
pub
use
self
::
visitor
::{
visit_results
,
ResultsVisitor
};
pub
use
self
::
visitor
::{
BorrowckFlowState
,
BorrowckResults
};
/// A dataflow analysis that has converged to fixpoint.
pub
struct
Results
<
'tcx
,
A
>
where
A
:
Analysis
<
'tcx
>
,
{
pub
analysis
:
A
,
entry_sets
:
IndexVec
<
BasicBlock
,
BitSet
<
A
::
Idx
>>
,
}
impl
<
A
>
Results
<
'tcx
,
A
>
where
A
:
Analysis
<
'tcx
>
,
{
/// Creates a `ResultsCursor` that can inspect these `Results`.
pub
fn
into_results_cursor
(
self
,
body
:
&
'mir
mir
::
Body
<
'tcx
>
)
->
ResultsCursor
<
'mir
,
'tcx
,
A
>
{
ResultsCursor
::
new
(
body
,
self
)
}
/// Gets the entry set for the given block.
pub
fn
entry_set_for_block
(
&
self
,
block
:
BasicBlock
)
->
&
BitSet
<
A
::
Idx
>
{
&
self
.entry_sets
[
block
]
}
pub
fn
visit_with
(
&
self
,
body
:
&
'mir
mir
::
Body
<
'tcx
>
,
blocks
:
impl
IntoIterator
<
Item
=
BasicBlock
>
,
vis
:
&
mut
impl
ResultsVisitor
<
'mir
,
'tcx
,
FlowState
=
BitSet
<
A
::
Idx
>>
,
)
{
visit_results
(
body
,
blocks
,
self
,
vis
)
}
pub
fn
visit_in_rpo_with
(
&
self
,
body
:
&
'mir
mir
::
Body
<
'tcx
>
,
vis
:
&
mut
impl
ResultsVisitor
<
'mir
,
'tcx
,
FlowState
=
BitSet
<
A
::
Idx
>>
,
)
{
let
blocks
=
mir
::
traversal
::
reverse_postorder
(
body
);
visit_results
(
body
,
blocks
.map
(|(
bb
,
_
)|
bb
),
self
,
vis
)
}
}
/// Parameterization for the precise form of data flow that is used.
///
/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
...
...
@@ -144,6 +105,9 @@ pub trait AnalysisDomain<'tcx>: BottomValue {
/// The type of the elements in the state vector.
type
Idx
:
Idx
;
/// The direction of this analyis. Either `Forward` or `Backward`.
type
Direction
:
Direction
=
Forward
;
/// A descriptive name for this analysis. Used only for debugging.
///
/// This name should be brief and contain no spaces, periods or other characters that are not
...
...
@@ -155,6 +119,13 @@ pub trait AnalysisDomain<'tcx>: BottomValue {
/// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
/// analysis.
///
/// For backward analyses, initial state besides the bottom value is not yet supported. Trying
/// to mutate the initial state will result in a panic.
//
// FIXME: For backward dataflow analyses, the initial state should be applied to every basic
// block where control flow could exit the MIR body (e.g., those terminated with `return` or
// `resume`). It's not obvious how to handle `yield` points in generators, however.
fn
initialize_start_block
(
&
self
,
body
:
&
mir
::
Body
<
'tcx
>
,
state
:
&
mut
BitSet
<
Self
::
Idx
>
);
/// Prints an element in the state vector for debugging.
...
...
@@ -247,6 +218,8 @@ fn apply_yield_resume_effect(
///
/// Much like `apply_call_return_effect`, this effect is only propagated along a single
/// outgoing edge from this basic block.
///
/// FIXME: This class of effects is not supported for backward dataflow analyses.
fn
apply_discriminant_switch_effect
(
&
self
,
_
state
:
&
mut
BitSet
<
Self
::
Idx
>
,
...
...
@@ -338,7 +311,7 @@ fn call_return_effect(
/// See `Analysis::apply_yield_resume_effect`.
fn
yield_resume_effect
(
&
self
,
_
trans
:
&
mut
BitSet
<
Self
::
Idx
>
,
_
trans
:
&
mut
impl
GenKill
<
Self
::
Idx
>
,
_
resume_block
:
BasicBlock
,
_
resume_place
:
mir
::
Place
<
'tcx
>
,
)
{
...
...
@@ -520,5 +493,64 @@ fn kill(&mut self, elem: T) {
}
}
// NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order.
#[derive(Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord)]
pub
enum
Effect
{
/// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or
/// terminator).
Before
,
/// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator).
Primary
,
}
impl
Effect
{
pub
const
fn
at_index
(
self
,
statement_index
:
usize
)
->
EffectIndex
{
EffectIndex
{
effect
:
self
,
statement_index
}
}
}
#[derive(Clone,
Copy,
Debug,
PartialEq,
Eq)]
pub
struct
EffectIndex
{
statement_index
:
usize
,
effect
:
Effect
,
}
impl
EffectIndex
{
fn
next_in_forward_order
(
self
)
->
Self
{
match
self
.effect
{
Effect
::
Before
=>
Effect
::
Primary
.at_index
(
self
.statement_index
),
Effect
::
Primary
=>
Effect
::
Before
.at_index
(
self
.statement_index
+
1
),
}
}
fn
next_in_backward_order
(
self
)
->
Self
{
match
self
.effect
{
Effect
::
Before
=>
Effect
::
Primary
.at_index
(
self
.statement_index
),
Effect
::
Primary
=>
Effect
::
Before
.at_index
(
self
.statement_index
-
1
),
}
}
/// Returns `true` if the effect at `self` should be applied eariler than the effect at `other`
/// in forward order.
fn
precedes_in_forward_order
(
self
,
other
:
Self
)
->
bool
{
let
ord
=
self
.statement_index
.cmp
(
&
other
.statement_index
)
.then_with
(||
self
.effect
.cmp
(
&
other
.effect
));
ord
==
Ordering
::
Less
}
/// Returns `true` if the effect at `self` should be applied earlier than the effect at `other`
/// in backward order.
fn
precedes_in_backward_order
(
self
,
other
:
Self
)
->
bool
{
let
ord
=
other
.statement_index
.cmp
(
&
self
.statement_index
)
.then_with
(||
self
.effect
.cmp
(
&
other
.effect
));
ord
==
Ordering
::
Less
}
}
#[cfg(test)]
mod
tests
;
src/librustc_mir/dataflow/framework/tests.rs
浏览文件 @
c68d710d
//! A test for the logic that updates the state in a `ResultsCursor` during seek.
use
std
::
marker
::
PhantomData
;
use
rustc_index
::
bit_set
::
BitSet
;
use
rustc_index
::
vec
::
IndexVec
;
use
rustc_middle
::
mir
::{
self
,
BasicBlock
,
Location
};
...
...
@@ -9,16 +11,6 @@
use
super
::
*
;
use
crate
::
dataflow
::
BottomValue
;
/// Returns `true` if the given location points to a `Call` terminator that can return
/// successfully.
fn
is_call_terminator_non_diverging
(
body
:
&
mir
::
Body
<
'_
>
,
loc
:
Location
)
->
bool
{
loc
==
body
.terminator_loc
(
loc
.block
)
&&
matches!
(
body
[
loc
.block
]
.terminator
()
.kind
,
mir
::
TerminatorKind
::
Call
{
destination
:
Some
(
_
),
..
}
)
}
/// Creates a `mir::Body` with a few disconnected basic blocks.
///
/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not
...
...
@@ -79,20 +71,20 @@ fn mock_body() -> mir::Body<'static> {
/// | Location | Before | After |
/// |------------------------|-------------------|--------|
/// | (on_entry) | {102} ||
/// |
S
tatement 0 | +0 | +1 |
/// |
s
tatement 0 | +0 | +1 |
/// | statement 1 | +2 | +3 |
/// | `Call` terminator | +4 | +5 |
/// | (on unwind) | {102,0,1,2,3,4,5} ||
/// | (on successful return) | +6 ||
///
/// The `102` in the block's entry set is derived from the basic block index and ensures that the
/// expected state is unique across all basic blocks. Remember, it is generated by
/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint.
struct
MockAnalysis
<
'tcx
>
{
struct
MockAnalysis
<
'tcx
,
D
>
{
body
:
&
'tcx
mir
::
Body
<
'tcx
>
,
dir
:
PhantomData
<
D
>
,
}
impl
MockAnalysis
<
'tcx
>
{
impl
<
D
:
Direction
>
MockAnalysis
<
'tcx
,
D
>
{
const
BASIC_BLOCK_OFFSET
:
usize
=
100
;
/// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to
...
...
@@ -115,25 +107,14 @@ fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
}
/// Returns the index that should be added to the dataflow state at the given target.
///
/// This index is only unique within a given basic block. `SeekAfter` and
/// `SeekAfterAssumeCallReturns` have the same effect unless `target` is a `Call` terminator.
fn
effect_at_target
(
&
self
,
target
:
SeekTarget
)
->
Option
<
usize
>
{
use
SeekTarget
::
*
;
let
idx
=
match
target
{
BlockStart
(
_
)
=>
return
None
,
AfterAssumeCallReturns
(
loc
)
if
is_call_terminator_non_diverging
(
self
.body
,
loc
)
=>
{
loc
.statement_index
*
2
+
2
}
Before
(
loc
)
=>
loc
.statement_index
*
2
,
After
(
loc
)
|
AfterAssumeCallReturns
(
loc
)
=>
loc
.statement_index
*
2
+
1
,
fn
effect
(
&
self
,
loc
:
EffectIndex
)
->
usize
{
let
idx
=
match
loc
.effect
{
Effect
::
Before
=>
loc
.statement_index
*
2
,
Effect
::
Primary
=>
loc
.statement_index
*
2
+
1
,
};
assert
!
(
idx
<
Self
::
BASIC_BLOCK_OFFSET
,
"Too many statements in basic block"
);
Some
(
idx
)
idx
}
/// Returns the expected state at the given `SeekTarget`.
...
...
@@ -143,27 +124,48 @@ fn effect_at_target(&self, target: SeekTarget) -> Option<usize> {
/// basic block.
///
/// For example, the expected state when calling
/// `seek_before(Location { block: 2, statement_index: 2 })` would be `[102, 0, 1, 2, 3, 4]`.
/// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })`
/// would be `[102, 0, 1, 2, 3, 4]`.
fn
expected_state_at_target
(
&
self
,
target
:
SeekTarget
)
->
BitSet
<
usize
>
{
let
block
=
target
.block
();
let
mut
ret
=
BitSet
::
new_empty
(
self
.bits_per_block
(
self
.body
));
ret
.insert
(
Self
::
BASIC_BLOCK_OFFSET
+
target
.block
()
.index
());
ret
.insert
(
Self
::
BASIC_BLOCK_OFFSET
+
block
.index
());
if
let
Some
(
target_effect
)
=
self
.effect_at_target
(
target
)
{
for
i
in
0
..=
target_effect
{
ret
.insert
(
i
);
let
target
=
match
target
{
SeekTarget
::
BlockEntry
{
..
}
=>
return
ret
,
SeekTarget
::
Before
(
loc
)
=>
Effect
::
Before
.at_index
(
loc
.statement_index
),
SeekTarget
::
After
(
loc
)
=>
Effect
::
Primary
.at_index
(
loc
.statement_index
),
};
let
mut
pos
=
if
D
::
is_forward
()
{
Effect
::
Before
.at_index
(
0
)
}
else
{
Effect
::
Before
.at_index
(
self
.body
[
block
]
.statements
.len
())
};
loop
{
ret
.insert
(
self
.effect
(
pos
));
if
pos
==
target
{
return
ret
;
}
}
ret
if
D
::
is_forward
()
{
pos
=
pos
.next_in_forward_order
();
}
else
{
pos
=
pos
.next_in_backward_order
();
}
}
}
}
impl
BottomValue
for
MockAnalysis
<
'tcx
>
{
impl
<
D
:
Direction
>
BottomValue
for
MockAnalysis
<
'tcx
,
D
>
{
const
BOTTOM_VALUE
:
bool
=
false
;
}
impl
AnalysisDomain
<
'tcx
>
for
MockAnalysis
<
'tcx
>
{
impl
<
D
:
Direction
>
AnalysisDomain
<
'tcx
>
for
MockAnalysis
<
'tcx
,
D
>
{
type
Idx
=
usize
;
type
Direction
=
D
;
const
NAME
:
&
'static
str
=
"mock"
;
...
...
@@ -176,14 +178,14 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>)
}
}
impl
Analysis
<
'tcx
>
for
MockAnalysis
<
'tcx
>
{
impl
<
D
:
Direction
>
Analysis
<
'tcx
>
for
MockAnalysis
<
'tcx
,
D
>
{
fn
apply_statement_effect
(
&
self
,
state
:
&
mut
BitSet
<
Self
::
Idx
>
,
_
statement
:
&
mir
::
Statement
<
'tcx
>
,
location
:
Location
,
)
{
let
idx
=
self
.effect
_at_target
(
SeekTarget
::
After
(
location
))
.unwrap
(
);
let
idx
=
self
.effect
(
Effect
::
Primary
.at_index
(
location
.statement_index
)
);
assert
!
(
state
.insert
(
idx
));
}
...
...
@@ -193,7 +195,7 @@ fn apply_before_statement_effect(
_
statement
:
&
mir
::
Statement
<
'tcx
>
,
location
:
Location
,
)
{
let
idx
=
self
.effect
_at_target
(
SeekTarget
::
Before
(
location
))
.unwrap
(
);
let
idx
=
self
.effect
(
Effect
::
Before
.at_index
(
location
.statement_index
)
);
assert
!
(
state
.insert
(
idx
));
}
...
...
@@ -203,7 +205,7 @@ fn apply_terminator_effect(
_
terminator
:
&
mir
::
Terminator
<
'tcx
>
,
location
:
Location
,
)
{
let
idx
=
self
.effect
_at_target
(
SeekTarget
::
After
(
location
))
.unwrap
(
);
let
idx
=
self
.effect
(
Effect
::
Primary
.at_index
(
location
.statement_index
)
);
assert
!
(
state
.insert
(
idx
));
}
...
...
@@ -213,30 +215,26 @@ fn apply_before_terminator_effect(
_
terminator
:
&
mir
::
Terminator
<
'tcx
>
,
location
:
Location
,
)
{
let
idx
=
self
.effect
_at_target
(
SeekTarget
::
Before
(
location
))
.unwrap
(
);
let
idx
=
self
.effect
(
Effect
::
Before
.at_index
(
location
.statement_index
)
);
assert
!
(
state
.insert
(
idx
));
}
fn
apply_call_return_effect
(
&
self
,
state
:
&
mut
BitSet
<
Self
::
Idx
>
,
block
:
BasicBlock
,
_
state
:
&
mut
BitSet
<
Self
::
Idx
>
,
_
block
:
BasicBlock
,
_
func
:
&
mir
::
Operand
<
'tcx
>
,
_
args
:
&
[
mir
::
Operand
<
'tcx
>
],
_
return_place
:
mir
::
Place
<
'tcx
>
,
)
{
let
location
=
self
.body
.terminator_loc
(
block
);
let
idx
=
self
.effect_at_target
(
SeekTarget
::
AfterAssumeCallReturns
(
location
))
.unwrap
();
assert
!
(
state
.insert
(
idx
));
}
}
#[derive(Clone,
Copy,
Debug,
PartialEq,
Eq)]
enum
SeekTarget
{
Block
Start
(
BasicBlock
),
Block
Entry
(
BasicBlock
),
Before
(
Location
),
After
(
Location
),
AfterAssumeCallReturns
(
Location
),
}
impl
SeekTarget
{
...
...
@@ -244,59 +242,35 @@ fn block(&self) -> BasicBlock {
use
SeekTarget
::
*
;
match
*
self
{
Block
Start
(
block
)
=>
block
,
Before
(
loc
)
|
After
(
loc
)
|
AfterAssumeCallReturns
(
loc
)
=>
loc
.block
,
Block
Entry
(
block
)
=>
block
,
Before
(
loc
)
|
After
(
loc
)
=>
loc
.block
,
}
}
/// An iterator over all possible `SeekTarget`s in a given block in order, starting with
/// `BlockStart`.
///
/// This includes both `After` and `AfterAssumeCallReturns` for every `Location`.
/// `BlockEntry`.
fn
iter_in_block
(
body
:
&
mir
::
Body
<
'_
>
,
block
:
BasicBlock
)
->
impl
Iterator
<
Item
=
Self
>
{
let
statements_and_terminator
=
(
0
..=
body
[
block
]
.statements
.len
())
.flat_map
(|
i
|
(
0
..
3
)
.map
(
move
|
j
|
(
i
,
j
)))
.flat_map
(|
i
|
(
0
..
2
)
.map
(
move
|
j
|
(
i
,
j
)))
.map
(
move
|(
i
,
kind
)|
{
let
loc
=
Location
{
block
,
statement_index
:
i
};
match
kind
{
0
=>
SeekTarget
::
Before
(
loc
),
1
=>
SeekTarget
::
After
(
loc
),
2
=>
SeekTarget
::
AfterAssumeCallReturns
(
loc
),
_
=>
unreachable!
(),
}
});
std
::
iter
::
once
(
SeekTarget
::
Block
Start
(
block
))
.chain
(
statements_and_terminator
)
std
::
iter
::
once
(
SeekTarget
::
Block
Entry
(
block
))
.chain
(
statements_and_terminator
)
}
}
#[test]
fn
cursor_seek
()
{
let
body
=
mock_body
();
let
body
=
&
body
;
let
analysis
=
MockAnalysis
{
body
};
fn
test_cursor
<
D
:
Direction
>
(
analysis
:
MockAnalysis
<
'tcx
,
D
>
)
{
let
body
=
analysis
.body
;
let
mut
cursor
=
Results
{
entry_sets
:
analysis
.mock_entry_sets
(),
analysis
}
.into_results_cursor
(
body
);
// Sanity check: the mock call return effect is unique and actually being applied.
let
call_terminator_loc
=
Location
{
block
:
BasicBlock
::
from_usize
(
2
),
statement_index
:
2
};
assert
!
(
is_call_terminator_non_diverging
(
body
,
call_terminator_loc
));
let
call_return_effect
=
cursor
.analysis
()
.effect_at_target
(
SeekTarget
::
AfterAssumeCallReturns
(
call_terminator_loc
))
.unwrap
();
assert_ne!
(
call_return_effect
,
cursor
.analysis
()
.effect_at_target
(
SeekTarget
::
After
(
call_terminator_loc
))
.unwrap
()
);
cursor
.seek_after
(
call_terminator_loc
);
assert
!
(
!
cursor
.get
()
.contains
(
call_return_effect
));
cursor
.seek_after_assume_success
(
call_terminator_loc
);
assert
!
(
cursor
.get
()
.contains
(
call_return_effect
));
let
every_target
=
||
{
body
.basic_blocks
()
.iter_enumerated
()
...
...
@@ -307,10 +281,9 @@ fn cursor_seek() {
use
SeekTarget
::
*
;
match
targ
{
BlockStart
(
block
)
=>
cursor
.seek_to_block_start
(
block
),
Before
(
loc
)
=>
cursor
.seek_before
(
loc
),
After
(
loc
)
=>
cursor
.seek_after
(
loc
),
AfterAssumeCallReturns
(
loc
)
=>
cursor
.seek_after_assume_success
(
loc
),
BlockEntry
(
block
)
=>
cursor
.seek_to_block_entry
(
block
),
Before
(
loc
)
=>
cursor
.seek_before_primary_effect
(
loc
),
After
(
loc
)
=>
cursor
.seek_after_primary_effect
(
loc
),
}
assert_eq!
(
cursor
.get
(),
&
cursor
.analysis
()
.expected_state_at_target
(
targ
));
...
...
@@ -325,8 +298,26 @@ fn cursor_seek() {
seek_to_target
(
from
);
for
to
in
every_target
()
{
dbg!
(
from
);
dbg!
(
to
);
seek_to_target
(
to
);
seek_to_target
(
from
);
}
}
}
#[test]
fn
backward_cursor
()
{
let
body
=
mock_body
();
let
body
=
&
body
;
let
analysis
=
MockAnalysis
{
body
,
dir
:
PhantomData
::
<
Backward
>
};
test_cursor
(
analysis
)
}
#[test]
fn
forward_cursor
()
{
let
body
=
mock_body
();
let
body
=
&
body
;
let
analysis
=
MockAnalysis
{
body
,
dir
:
PhantomData
::
<
Forward
>
};
test_cursor
(
analysis
)
}
src/librustc_mir/dataflow/framework/visitor.rs
浏览文件 @
c68d710d
use
rustc_index
::
bit_set
::
BitSet
;
use
rustc_middle
::
mir
::{
self
,
BasicBlock
,
Location
};
use
super
::{
Analysis
,
Results
};
use
super
::{
Analysis
,
Direction
,
Results
};
use
crate
::
dataflow
::
impls
::{
borrows
::
Borrows
,
EverInitializedPlaces
,
MaybeUninitializedPlaces
};
/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
/// dataflow state at that location.
pub
fn
visit_results
<
F
>
(
pub
fn
visit_results
<
F
,
V
>
(
body
:
&
'mir
mir
::
Body
<
'tcx
>
,
blocks
:
impl
IntoIterator
<
Item
=
BasicBlock
>
,
results
:
&
impl
ResultsVisitable
<
'tcx
,
FlowState
=
F
>
,
results
:
&
V
,
vis
:
&
mut
impl
ResultsVisitor
<
'mir
,
'tcx
,
FlowState
=
F
>
,
)
{
)
where
V
:
ResultsVisitable
<
'tcx
,
FlowState
=
F
>
,
{
let
mut
state
=
results
.new_flow_state
(
body
);
for
block
in
blocks
{
let
block_data
=
&
body
[
block
];
results
.reset_to_block_start
(
&
mut
state
,
block
);
for
(
statement_index
,
stmt
)
in
block_data
.statements
.iter
()
.enumerate
()
{
let
loc
=
Location
{
block
,
statement_index
};
results
.reconstruct_before_statement_effect
(
&
mut
state
,
stmt
,
loc
);
vis
.visit_statement
(
&
state
,
stmt
,
loc
);
results
.reconstruct_statement_effect
(
&
mut
state
,
stmt
,
loc
);
vis
.visit_statement_exit
(
&
state
,
stmt
,
loc
);
}
let
loc
=
body
.terminator_loc
(
block
);
let
term
=
block_data
.terminator
();
results
.reconstruct_before_terminator_effect
(
&
mut
state
,
term
,
loc
);
vis
.visit_terminator
(
&
state
,
term
,
loc
);
results
.reconstruct_terminator_effect
(
&
mut
state
,
term
,
loc
);
vis
.visit_terminator_exit
(
&
state
,
term
,
loc
);
V
::
Direction
::
visit_results_in_block
(
&
mut
state
,
block
,
block_data
,
results
,
vis
);
}
}
pub
trait
ResultsVisitor
<
'mir
,
'tcx
>
{
type
FlowState
;
fn
visit_block_start
(
&
mut
self
,
_
state
:
&
Self
::
FlowState
,
_
block_data
:
&
'mir
mir
::
BasicBlockData
<
'tcx
>
,
_
block
:
BasicBlock
,
)
{
}
/// Called with the `before_statement_effect` of the given statement applied to `state` but not
/// its `statement_effect`.
fn
visit_statement
(
fn
visit_statement
_before_primary_effect
(
&
mut
self
,
_
state
:
&
Self
::
FlowState
,
_
statement
:
&
'mir
mir
::
Statement
<
'tcx
>
,
...
...
@@ -54,7 +45,7 @@ fn visit_statement(
/// Called with both the `before_statement_effect` and the `statement_effect` of the given
/// statement applied to `state`.
fn
visit_statement_
exi
t
(
fn
visit_statement_
after_primary_effec
t
(
&
mut
self
,
_
state
:
&
Self
::
FlowState
,
_
statement
:
&
'mir
mir
::
Statement
<
'tcx
>
,
...
...
@@ -64,7 +55,7 @@ fn visit_statement_exit(
/// Called with the `before_terminator_effect` of the given terminator applied to `state` but not
/// its `terminator_effect`.
fn
visit_terminator
(
fn
visit_terminator
_before_primary_effect
(
&
mut
self
,
_
state
:
&
Self
::
FlowState
,
_
terminator
:
&
'mir
mir
::
Terminator
<
'tcx
>
,
...
...
@@ -76,13 +67,21 @@ fn visit_terminator(
/// terminator applied to `state`.
///
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
fn
visit_terminator_
exi
t
(
fn
visit_terminator_
after_primary_effec
t
(
&
mut
self
,
_
state
:
&
Self
::
FlowState
,
_
terminator
:
&
'mir
mir
::
Terminator
<
'tcx
>
,
_
location
:
Location
,
)
{
}
fn
visit_block_end
(
&
mut
self
,
_
state
:
&
Self
::
FlowState
,
_
block_data
:
&
'mir
mir
::
BasicBlockData
<
'tcx
>
,
_
block
:
BasicBlock
,
)
{
}
}
/// Things that can be visited by a `ResultsVisitor`.
...
...
@@ -90,15 +89,16 @@ fn visit_terminator_exit(
/// This trait exists so that we can visit the results of multiple dataflow analyses simultaneously.
/// DO NOT IMPLEMENT MANUALLY. Instead, use the `impl_visitable` macro below.
pub
trait
ResultsVisitable
<
'tcx
>
{
type
Direction
:
Direction
;
type
FlowState
;
/// Creates an empty `FlowState` to hold the transient state for these dataflow results.
///
/// The value of the newly created `FlowState` will be overwritten by `reset_to_block_
start
`
/// The value of the newly created `FlowState` will be overwritten by `reset_to_block_
entry
`
/// before it can be observed by a `ResultsVisitor`.
fn
new_flow_state
(
&
self
,
body
:
&
mir
::
Body
<
'tcx
>
)
->
Self
::
FlowState
;
fn
reset_to_block_
start
(
&
self
,
state
:
&
mut
Self
::
FlowState
,
block
:
BasicBlock
);
fn
reset_to_block_
entry
(
&
self
,
state
:
&
mut
Self
::
FlowState
,
block
:
BasicBlock
);
fn
reconstruct_before_statement_effect
(
&
self
,
...
...
@@ -135,11 +135,13 @@ impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
{
type
FlowState
=
BitSet
<
A
::
Idx
>
;
type
Direction
=
A
::
Direction
;
fn
new_flow_state
(
&
self
,
body
:
&
mir
::
Body
<
'tcx
>
)
->
Self
::
FlowState
{
BitSet
::
new_empty
(
self
.analysis
.bits_per_block
(
body
))
}
fn
reset_to_block_
start
(
&
self
,
state
:
&
mut
Self
::
FlowState
,
block
:
BasicBlock
)
{
fn
reset_to_block_
entry
(
&
self
,
state
:
&
mut
Self
::
FlowState
,
block
:
BasicBlock
)
{
state
.overwrite
(
&
self
.entry_set_for_block
(
block
));
}
...
...
@@ -204,10 +206,11 @@ pub struct BorrowckAnalyses<B, U, E> {
(
$
(
$T:ident
{
$
(
$field:ident
:
$A:ident
),
*
$
(,)
?
}
)
*
)
=>
{
$
(
impl
<
'tcx
,
$
(
$A
),
*>
ResultsVisitable
<
'tcx
>
for
$T
<
$
(
Results
<
'tcx
,
$A
>
),
*>
impl
<
'tcx
,
$
(
$A
),
*
,
D
:
Direction
>
ResultsVisitable
<
'tcx
>
for
$T
<
$
(
Results
<
'tcx
,
$A
>
),
*>
where
$
(
$A
:
Analysis
<
'tcx
>
,
)
*
$
(
$A
:
Analysis
<
'tcx
,
Direction
=
D
>
,
)
*
{
type
Direction
=
D
;
type
FlowState
=
$T
<
$
(
BitSet
<
$A
::
Idx
>
),
*>
;
fn
new_flow_state
(
&
self
,
body
:
&
mir
::
Body
<
'tcx
>
)
->
Self
::
FlowState
{
...
...
@@ -216,12 +219,12 @@ fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
}
}
fn
reset_to_block_
start
(
fn
reset_to_block_
entry
(
&
self
,
state
:
&
mut
Self
::
FlowState
,
block
:
BasicBlock
,
)
{
$
(
state
.
$field
.overwrite
(
&
self
.
$field
.entry_set
s
[
block
]
);
)
*
$
(
state
.
$field
.overwrite
(
&
self
.
$field
.entry_set
_for_block
(
block
)
);
)
*
}
fn
reconstruct_before_statement_effect
(
...
...
src/librustc_mir/dataflow/impls/storage_liveness.rs
浏览文件 @
c68d710d
...
...
@@ -250,7 +250,7 @@ fn call_return_effect(
fn
yield_resume_effect
(
&
self
,
trans
:
&
mut
BitSet
<
Self
::
Idx
>
,
trans
:
&
mut
impl
GenKill
<
Self
::
Idx
>
,
_
resume_block
:
BasicBlock
,
resume_place
:
mir
::
Place
<
'tcx
>
,
)
{
...
...
@@ -283,7 +283,7 @@ impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
fn
visit_local
(
&
mut
self
,
local
:
&
Local
,
context
:
PlaceContext
,
loc
:
Location
)
{
if
PlaceContext
::
NonMutatingUse
(
NonMutatingUseContext
::
Move
)
==
context
{
let
mut
borrowed_locals
=
self
.borrowed_locals
.borrow_mut
();
borrowed_locals
.seek_before
(
loc
);
borrowed_locals
.seek_before
_primary_effect
(
loc
);
if
!
borrowed_locals
.contains
(
*
local
)
{
self
.trans
.kill
(
*
local
);
}
...
...
src/librustc_mir/dataflow/mod.rs
浏览文件 @
c68d710d
...
...
@@ -4,8 +4,9 @@
pub
(
crate
)
use
self
::
drop_flag_effects
::
*
;
pub
use
self
::
framework
::{
visit_results
,
Analysis
,
AnalysisDomain
,
BorrowckFlowState
,
BorrowckResults
,
BottomValue
,
Engine
,
GenKill
,
GenKillAnalysis
,
Results
,
ResultsCursor
,
ResultsRefCursor
,
ResultsVisitor
,
visit_results
,
Analysis
,
AnalysisDomain
,
Backward
,
BorrowckFlowState
,
BorrowckResults
,
BottomValue
,
Engine
,
Forward
,
GenKill
,
GenKillAnalysis
,
Results
,
ResultsCursor
,
ResultsRefCursor
,
ResultsVisitor
,
};
pub
use
self
::
impls
::{
borrows
::
Borrows
,
DefinitelyInitializedPlaces
,
EverInitializedPlaces
,
MaybeBorrowedLocals
,
...
...
src/librustc_mir/lib.rs
浏览文件 @
c68d710d
...
...
@@ -9,8 +9,9 @@
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(const_if_match)]
#![feature(const_fn)]
#![feature(const_if_match)]
#![feature(const_loop)]
#![feature(const_panic)]
#![feature(crate_visibility_modifier)]
#![feature(decl_macro)]
...
...
@@ -22,6 +23,7 @@
#![feature(trusted_len)]
#![feature(try_blocks)]
#![feature(associated_type_bounds)]
#![feature(associated_type_defaults)]
#![feature(range_is_empty)]
#![feature(stmt_expr_attributes)]
#![feature(trait_alias)]
...
...
src/librustc_mir/transform/check_consts/validation.rs
浏览文件 @
c68d710d
...
...
@@ -61,7 +61,7 @@ fn indirectly_mutable(
.into_results_cursor
(
&
body
)
});
indirectly_mutable
.seek_before
(
location
);
indirectly_mutable
.seek_before
_primary_effect
(
location
);
indirectly_mutable
.get
()
.contains
(
local
)
}
...
...
@@ -88,7 +88,7 @@ fn needs_drop(
.into_results_cursor
(
&
body
)
});
needs_drop
.seek_before
(
location
);
needs_drop
.seek_before
_primary_effect
(
location
);
needs_drop
.get
()
.contains
(
local
)
||
self
.indirectly_mutable
(
ccx
,
local
,
location
)
}
...
...
@@ -115,7 +115,7 @@ fn has_mut_interior(
.into_results_cursor
(
&
body
)
});
has_mut_interior
.seek_before
(
location
);
has_mut_interior
.seek_before
_primary_effect
(
location
);
has_mut_interior
.get
()
.contains
(
local
)
||
self
.indirectly_mutable
(
ccx
,
local
,
location
)
}
...
...
src/librustc_mir/transform/elaborate_drops.rs
浏览文件 @
c68d710d
...
...
@@ -101,7 +101,7 @@ fn find_dead_unwinds<'tcx>(
}
};
flow_inits
.seek_before
(
body
.terminator_loc
(
bb
));
flow_inits
.seek_before
_primary_effect
(
body
.terminator_loc
(
bb
));
debug!
(
"find_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}"
,
bb
,
...
...
@@ -131,8 +131,8 @@ struct InitializationData<'mir, 'tcx> {
impl
InitializationData
<
'_
,
'_
>
{
fn
seek_before
(
&
mut
self
,
loc
:
Location
)
{
self
.inits
.seek_before
(
loc
);
self
.uninits
.seek_before
(
loc
);
self
.inits
.seek_before
_primary_effect
(
loc
);
self
.uninits
.seek_before
_primary_effect
(
loc
);
}
fn
maybe_live_dead
(
&
self
,
path
:
MovePathIndex
)
->
(
bool
,
bool
)
{
...
...
src/librustc_mir/transform/generator.rs
浏览文件 @
c68d710d
...
...
@@ -490,21 +490,16 @@ fn locals_live_across_suspend_points(
// If a borrow is converted to a raw reference, we must also assume that it lives
// forever. Note that the final liveness is still bounded by the storage liveness
// of the local, which happens using the `intersect` operation below.
borrowed_locals_cursor
.seek_before
(
loc
);
borrowed_locals_cursor
.seek_before
_primary_effect
(
loc
);
liveness
.outs
[
block
]
.union
(
borrowed_locals_cursor
.get
());
}
storage_live
.seek_before
(
loc
);
let
mut
storage_liveness
=
storage_live
.get
()
.clone
();
// Later passes handle the generator's `self` argument separately.
storage_liveness
.remove
(
SELF_ARG
);
// Store the storage liveness for later use so we can restore the state
// after a suspension point
storage_liveness_map
[
block
]
=
Some
(
storage_liveness
);
storage_live
.seek_before_primary_effect
(
loc
);
storage_liveness_map
[
block
]
=
Some
(
storage_live
.get
()
.clone
());
requires_storage_cursor
.seek_before
(
loc
);
requires_storage_cursor
.seek_before
_primary_effect
(
loc
);
let
storage_required
=
requires_storage_cursor
.get
()
.clone
();
// Locals live are live at this point only if they are used across
...
...
src/librustc_mir/transform/rustc_peek.rs
浏览文件 @
c68d710d
...
...
@@ -126,7 +126,7 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>(
mir
::
Rvalue
::
Use
(
mir
::
Operand
::
Move
(
place
)
|
mir
::
Operand
::
Copy
(
place
)),
)
=>
{
let
loc
=
Location
{
block
:
bb
,
statement_index
};
cursor
.seek_before
(
loc
);
cursor
.seek_before
_primary_effect
(
loc
);
let
state
=
cursor
.get
();
results
.analysis
.peek_at
(
tcx
,
*
place
,
state
,
call
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录