Skip to content

Commit 7b8171e

Browse files
committed
Added liveness analysis for protocols, and removed warnings about empty states.
1 parent c858eb0 commit 7b8171e

File tree

10 files changed

+130
-19
lines changed

10 files changed

+130
-19
lines changed

src/libcore/future.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ fn from_port<A:send>(-port: future_pipe::client::waiting<A>) -> future<A> {
8181
port_ <-> *port;
8282
let port = option::unwrap(port_);
8383
alt recv(port) {
84-
future_pipe::completed(data, _next) { #move(data) }
84+
future_pipe::completed(data) { #move(data) }
8585
}
8686
}
8787
}
@@ -135,10 +135,8 @@ fn with<A,B>(future: future<A>, blk: fn(A) -> B) -> B {
135135

136136
proto! future_pipe {
137137
waiting:recv<T:send> {
138-
completed(T) -> terminated
138+
completed(T) -> !
139139
}
140-
141-
terminated:send { }
142140
}
143141

144142
#[test]

src/libstd/bitv.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,14 @@ impl methods for bitv {
253253
fn each(f: fn(bool) -> bool) { each(self, f) }
254254
fn each_storage(f: fn(&uint) -> bool) { each_storage(self, f) }
255255
fn eq_vec(v: ~[uint]) -> bool { eq_vec(self, v) }
256+
257+
fn ones(f: fn(uint) -> bool) {
258+
for uint::range(0, self.nbits) |i| {
259+
if self.get(i) {
260+
if !f(i) { break }
261+
}
262+
}
263+
}
256264
}
257265

258266
impl of to_str::to_str for bitv {

src/libsyntax/ext/pipes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
2727
// check for errors
2828
visit(proto, cx);
2929

30+
// do analysis
31+
liveness::analyze(proto, cx);
32+
3033
// compile
3134
base::mr_item(proto.compile(cx))
3235
}

src/libsyntax/ext/pipes/liveness.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
3+
Liveness analysis for protocols. This is useful for a lot of possible
4+
optimizations.
5+
6+
This analysis computes the "co-live" relationship between
7+
states. Co-live is defined inductively as follows.
8+
9+
1. u is co-live with v if u can transition to v in one message.
10+
11+
2. u is co-live with v if there exists a w such that u and w are
12+
co-live, w and v are co-live, and u and w have the same direction.
13+
14+
This relationship approximates when it is safe to store two states in
15+
the same memory location. If there is no u such u is co-live with
16+
itself, then the protocol is bounded.
17+
18+
(These assertions could use proofs)
19+
20+
In addition, this analysis does reachability, to warn when we have
21+
useless states.
22+
23+
The algorithm is a fixpoint computation. For each state, we initialize
24+
a bitvector containing whether it is co-live with each other state. At
25+
first we use rule (1) above to set each vector. Then we iterate
26+
updating the states using rule (2) until there are no changes.
27+
28+
*/
29+
30+
import dvec::extensions;
31+
32+
import std::bitv::{bitv, methods};
33+
34+
import proto::methods;
35+
import ast_builder::empty_span;
36+
37+
fn analyze(proto: protocol, _cx: ext_ctxt) {
38+
#debug("initializing colive analysis");
39+
let num_states = proto.num_states();
40+
let colive = do (copy proto.states).map_to_vec |state| {
41+
let bv = bitv(num_states, false);
42+
for state.reachable |s| {
43+
bv.set(s.id, true);
44+
}
45+
bv
46+
};
47+
48+
let mut i = 0;
49+
let mut changed = true;
50+
while changed {
51+
changed = false;
52+
#debug("colive iteration %?", i);
53+
for colive.eachi |i, this_colive| {
54+
let this = proto.get_state_by_id(i);
55+
for this_colive.ones |j| {
56+
let next = proto.get_state_by_id(j);
57+
if this.dir == next.dir {
58+
changed = changed || this_colive.union(colive[j]);
59+
}
60+
}
61+
}
62+
i += 1;
63+
}
64+
65+
#debug("colive analysis complete");
66+
67+
// Determine if we're bounded
68+
let mut self_live = ~[];
69+
for colive.eachi |i, bv| {
70+
if bv.get(i) {
71+
vec::push(self_live, proto.get_state_by_id(i))
72+
}
73+
}
74+
75+
if self_live.len() > 0 {
76+
let states = str::connect(self_live.map(|s| *s.name), ~" ");
77+
78+
#debug("protocol %s is unbounded due to loops involving: %s",
79+
*proto.name, states);
80+
81+
// Someday this will be configurable with a warning
82+
//cx.span_warn(empty_span(),
83+
// #fmt("protocol %s is unbounded due to loops \
84+
// involving these states: %s",
85+
// *proto.name,
86+
// states));
87+
}
88+
else {
89+
#debug("protocol %s is bounded. yay!", *proto.name);
90+
}
91+
}

src/libsyntax/ext/pipes/proto.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl methods for message {
5555

5656
enum state {
5757
state_(@{
58+
id: uint,
5859
name: ident,
5960
dir: direction,
6061
ty_params: ~[ast::ty_param],
@@ -81,6 +82,20 @@ impl methods for state {
8182
cx.ty_path_ast_builder
8283
(path(self.name).add_tys(cx.ty_vars(self.ty_params)))
8384
}
85+
86+
/// Iterate over the states that can be reached in one message
87+
/// from this state.
88+
fn reachable(f: fn(state) -> bool) {
89+
for self.messages.each |m| {
90+
alt m {
91+
message(_, _, _, some({state: id, _})) {
92+
let state = self.proto.get_state(id);
93+
if !f(state) { break }
94+
}
95+
_ { }
96+
}
97+
}
98+
}
8499
}
85100

86101
enum protocol {
@@ -104,6 +119,8 @@ impl methods for protocol {
104119
self.states.find(|i| i.name == name).get()
105120
}
106121

122+
fn get_state_by_id(id: uint) -> state { self.states[id] }
123+
107124
fn has_state(name: ident) -> bool {
108125
self.states.find(|i| i.name == name) != none
109126
}
@@ -113,6 +130,7 @@ impl methods for protocol {
113130
let messages = dvec();
114131

115132
let state = state_(@{
133+
id: self.states.len(),
116134
name: name,
117135
dir: dir,
118136
ty_params: ty_params,
@@ -127,6 +145,8 @@ impl methods for protocol {
127145
fn filename() -> ~str {
128146
~"proto://" + *self.name
129147
}
148+
149+
fn num_states() -> uint { self.states.len() }
130150
}
131151

132152
trait visitor<Tproto, Tstate, Tmessage> {

src/libsyntax/syntax.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,6 @@ mod ext {
8787
mod pipec;
8888
mod proto;
8989
mod check;
90+
mod liveness;
9091
}
9192
}

src/test/run-pass/pipe-detect-term.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,14 @@ import pipes::{try_recv, recv};
1111

1212
proto! oneshot {
1313
waiting:send {
14-
signal -> signaled
14+
signal -> !
1515
}
16-
17-
signaled:send { }
1816
}
1917

2018
fn main() {
2119
let iotask = uv::global_loop::get();
2220

23-
let c = pipes::spawn_service(oneshot::init, |p| {
21+
pipes::spawn_service(oneshot::init, |p| {
2422
alt try_recv(p) {
2523
some(*) { fail }
2624
none { }
@@ -36,8 +34,6 @@ fn main() {
3634

3735
// Make sure the right thing happens during failure.
3836
fn failtest() {
39-
let iotask = uv::global_loop::get();
40-
4137
let (c, p) = oneshot::init();
4238

4339
do task::spawn_with(c) |_c| {

src/test/run-pass/pipe-peek.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import std::uv;
66

77
proto! oneshot {
88
waiting:send {
9-
signal -> signaled
9+
signal -> !
1010
}
11-
12-
signaled:send { }
1311
}
1412

1513
fn main() {

src/test/run-pass/pipe-select.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ proto! oneshot {
1111
waiting:send {
1212
signal -> !
1313
}
14-
15-
signaled:send { }
1614
}
1715

1816
proto! stream {

src/test/run-pass/pipe-sleep.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ import pipes::recv;
77

88
proto! oneshot {
99
waiting:send {
10-
signal -> signaled
10+
signal -> !
1111
}
12-
13-
signaled:send { }
1412
}
1513

1614
fn main() {
@@ -19,7 +17,7 @@ fn main() {
1917
let c = pipes::spawn_service(oneshot::init, |p| { recv(p); });
2018

2119
let iotask = uv::global_loop::get();
22-
sleep(iotask, 5000);
20+
sleep(iotask, 500);
2321

2422
signal(c);
2523
}

0 commit comments

Comments
 (0)