diff options
| author | Eric Holk <eric.holk@gmail.com> | 2012-07-17 17:03:27 -0700 |
|---|---|---|
| committer | Eric Holk <eric.holk@gmail.com> | 2012-07-17 17:46:31 -0700 |
| commit | 7b8171ef2d03e4a2235b4bf0af255dab1b53bc25 (patch) | |
| tree | 54194e622a33eacd1c3e04db66304a7c1e60fad0 /src | |
| parent | c858eb06546ce2be77fd284f707f76cb820c6211 (diff) | |
| download | rust-7b8171ef2d03e4a2235b4bf0af255dab1b53bc25.tar.gz rust-7b8171ef2d03e4a2235b4bf0af255dab1b53bc25.zip | |
Added liveness analysis for protocols, and removed warnings about empty states.
Diffstat (limited to 'src')
| -rw-r--r-- | src/libcore/future.rs | 6 | ||||
| -rw-r--r-- | src/libstd/bitv.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/ext/pipes.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/ext/pipes/liveness.rs | 91 | ||||
| -rw-r--r-- | src/libsyntax/ext/pipes/proto.rs | 20 | ||||
| -rw-r--r-- | src/libsyntax/syntax.rc | 1 | ||||
| -rw-r--r-- | src/test/run-pass/pipe-detect-term.rs | 8 | ||||
| -rw-r--r-- | src/test/run-pass/pipe-peek.rs | 4 | ||||
| -rw-r--r-- | src/test/run-pass/pipe-select.rs | 2 | ||||
| -rw-r--r-- | src/test/run-pass/pipe-sleep.rs | 6 |
10 files changed, 130 insertions, 19 deletions
diff --git a/src/libcore/future.rs b/src/libcore/future.rs index f26643a6cab..f7efe70b5dc 100644 --- a/src/libcore/future.rs +++ b/src/libcore/future.rs @@ -81,7 +81,7 @@ fn from_port<A:send>(-port: future_pipe::client::waiting<A>) -> future<A> { port_ <-> *port; let port = option::unwrap(port_); alt recv(port) { - future_pipe::completed(data, _next) { #move(data) } + future_pipe::completed(data) { #move(data) } } } } @@ -135,10 +135,8 @@ fn with<A,B>(future: future<A>, blk: fn(A) -> B) -> B { proto! future_pipe { waiting:recv<T:send> { - completed(T) -> terminated + completed(T) -> ! } - - terminated:send { } } #[test] diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs index 74dadc556eb..1a3d5e08113 100644 --- a/src/libstd/bitv.rs +++ b/src/libstd/bitv.rs @@ -253,6 +253,14 @@ impl methods for bitv { fn each(f: fn(bool) -> bool) { each(self, f) } fn each_storage(f: fn(&uint) -> bool) { each_storage(self, f) } fn eq_vec(v: ~[uint]) -> bool { eq_vec(self, v) } + + fn ones(f: fn(uint) -> bool) { + for uint::range(0, self.nbits) |i| { + if self.get(i) { + if !f(i) { break } + } + } + } } impl of to_str::to_str for bitv { diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs index bea1c8cae0c..5a1462cea30 100644 --- a/src/libsyntax/ext/pipes.rs +++ b/src/libsyntax/ext/pipes.rs @@ -27,6 +27,9 @@ fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident, // check for errors visit(proto, cx); + // do analysis + liveness::analyze(proto, cx); + // compile base::mr_item(proto.compile(cx)) } diff --git a/src/libsyntax/ext/pipes/liveness.rs b/src/libsyntax/ext/pipes/liveness.rs new file mode 100644 index 00000000000..4f803992520 --- /dev/null +++ b/src/libsyntax/ext/pipes/liveness.rs @@ -0,0 +1,91 @@ +/* + +Liveness analysis for protocols. This is useful for a lot of possible +optimizations. + +This analysis computes the "co-live" relationship between +states. Co-live is defined inductively as follows. + +1. u is co-live with v if u can transition to v in one message. + +2. u is co-live with v if there exists a w such that u and w are +co-live, w and v are co-live, and u and w have the same direction. + +This relationship approximates when it is safe to store two states in +the same memory location. If there is no u such u is co-live with +itself, then the protocol is bounded. + +(These assertions could use proofs) + +In addition, this analysis does reachability, to warn when we have +useless states. + +The algorithm is a fixpoint computation. For each state, we initialize +a bitvector containing whether it is co-live with each other state. At +first we use rule (1) above to set each vector. Then we iterate +updating the states using rule (2) until there are no changes. + +*/ + +import dvec::extensions; + +import std::bitv::{bitv, methods}; + +import proto::methods; +import ast_builder::empty_span; + +fn analyze(proto: protocol, _cx: ext_ctxt) { + #debug("initializing colive analysis"); + let num_states = proto.num_states(); + let colive = do (copy proto.states).map_to_vec |state| { + let bv = bitv(num_states, false); + for state.reachable |s| { + bv.set(s.id, true); + } + bv + }; + + let mut i = 0; + let mut changed = true; + while changed { + changed = false; + #debug("colive iteration %?", i); + for colive.eachi |i, this_colive| { + let this = proto.get_state_by_id(i); + for this_colive.ones |j| { + let next = proto.get_state_by_id(j); + if this.dir == next.dir { + changed = changed || this_colive.union(colive[j]); + } + } + } + i += 1; + } + + #debug("colive analysis complete"); + + // Determine if we're bounded + let mut self_live = ~[]; + for colive.eachi |i, bv| { + if bv.get(i) { + vec::push(self_live, proto.get_state_by_id(i)) + } + } + + if self_live.len() > 0 { + let states = str::connect(self_live.map(|s| *s.name), ~" "); + + #debug("protocol %s is unbounded due to loops involving: %s", + *proto.name, states); + + // Someday this will be configurable with a warning + //cx.span_warn(empty_span(), + // #fmt("protocol %s is unbounded due to loops \ + // involving these states: %s", + // *proto.name, + // states)); + } + else { + #debug("protocol %s is bounded. yay!", *proto.name); + } +} \ No newline at end of file diff --git a/src/libsyntax/ext/pipes/proto.rs b/src/libsyntax/ext/pipes/proto.rs index 1c88732adb0..e9070c79b55 100644 --- a/src/libsyntax/ext/pipes/proto.rs +++ b/src/libsyntax/ext/pipes/proto.rs @@ -55,6 +55,7 @@ impl methods for message { enum state { state_(@{ + id: uint, name: ident, dir: direction, ty_params: ~[ast::ty_param], @@ -81,6 +82,20 @@ impl methods for state { cx.ty_path_ast_builder (path(self.name).add_tys(cx.ty_vars(self.ty_params))) } + + /// Iterate over the states that can be reached in one message + /// from this state. + fn reachable(f: fn(state) -> bool) { + for self.messages.each |m| { + alt m { + message(_, _, _, some({state: id, _})) { + let state = self.proto.get_state(id); + if !f(state) { break } + } + _ { } + } + } + } } enum protocol { @@ -104,6 +119,8 @@ impl methods for protocol { self.states.find(|i| i.name == name).get() } + fn get_state_by_id(id: uint) -> state { self.states[id] } + fn has_state(name: ident) -> bool { self.states.find(|i| i.name == name) != none } @@ -113,6 +130,7 @@ impl methods for protocol { let messages = dvec(); let state = state_(@{ + id: self.states.len(), name: name, dir: dir, ty_params: ty_params, @@ -127,6 +145,8 @@ impl methods for protocol { fn filename() -> ~str { ~"proto://" + *self.name } + + fn num_states() -> uint { self.states.len() } } trait visitor<Tproto, Tstate, Tmessage> { diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc index 9750f2d5bd1..10ff0d336c2 100644 --- a/src/libsyntax/syntax.rc +++ b/src/libsyntax/syntax.rc @@ -87,5 +87,6 @@ mod ext { mod pipec; mod proto; mod check; + mod liveness; } } diff --git a/src/test/run-pass/pipe-detect-term.rs b/src/test/run-pass/pipe-detect-term.rs index 633be6972d0..db8cde820ac 100644 --- a/src/test/run-pass/pipe-detect-term.rs +++ b/src/test/run-pass/pipe-detect-term.rs @@ -11,16 +11,14 @@ import pipes::{try_recv, recv}; proto! oneshot { waiting:send { - signal -> signaled + signal -> ! } - - signaled:send { } } fn main() { let iotask = uv::global_loop::get(); - let c = pipes::spawn_service(oneshot::init, |p| { + pipes::spawn_service(oneshot::init, |p| { alt try_recv(p) { some(*) { fail } none { } @@ -36,8 +34,6 @@ fn main() { // Make sure the right thing happens during failure. fn failtest() { - let iotask = uv::global_loop::get(); - let (c, p) = oneshot::init(); do task::spawn_with(c) |_c| { diff --git a/src/test/run-pass/pipe-peek.rs b/src/test/run-pass/pipe-peek.rs index 9a2451cd47a..45152627434 100644 --- a/src/test/run-pass/pipe-peek.rs +++ b/src/test/run-pass/pipe-peek.rs @@ -6,10 +6,8 @@ import std::uv; proto! oneshot { waiting:send { - signal -> signaled + signal -> ! } - - signaled:send { } } fn main() { diff --git a/src/test/run-pass/pipe-select.rs b/src/test/run-pass/pipe-select.rs index 5aab6b634b8..056b0cb477a 100644 --- a/src/test/run-pass/pipe-select.rs +++ b/src/test/run-pass/pipe-select.rs @@ -11,8 +11,6 @@ proto! oneshot { waiting:send { signal -> ! } - - signaled:send { } } proto! stream { diff --git a/src/test/run-pass/pipe-sleep.rs b/src/test/run-pass/pipe-sleep.rs index c4b44ff0be7..3ef0f4a4a64 100644 --- a/src/test/run-pass/pipe-sleep.rs +++ b/src/test/run-pass/pipe-sleep.rs @@ -7,10 +7,8 @@ import pipes::recv; proto! oneshot { waiting:send { - signal -> signaled + signal -> ! } - - signaled:send { } } fn main() { @@ -19,7 +17,7 @@ fn main() { let c = pipes::spawn_service(oneshot::init, |p| { recv(p); }); let iotask = uv::global_loop::get(); - sleep(iotask, 5000); + sleep(iotask, 500); signal(c); } \ No newline at end of file |
