From b0646e0749db0603c7d4460db6e17e9c27d0afb3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 18 Jun 2012 18:51:32 -0700 Subject: [PATCH] document liveness a bit better --- src/rustc/middle/liveness.rs | 62 ++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs index 981ec4d63e3..152623bc6be 100644 --- a/src/rustc/middle/liveness.rs +++ b/src/rustc/middle/liveness.rs @@ -1,4 +1,4 @@ -/* +#[doc = " A classic liveness analysis based on dataflow over the AST. Computes, for each local variable in a function, whether that variable is live @@ -42,7 +42,65 @@ Each field is assigned an index just as with local variables. A use of `self` is considered a use of all fields. A use of `self.f` is just a use of `f`. -*/ +# Implementation details + +The actual implementation contains two (nested) walks over the AST. +The outer walk has the job of building up the ir_maps instance for the +enclosing function. On the way down the tree, it identifies those AST +nodes and variable IDs that will be needed for the liveness analysis +and assigns them contiguous IDs. The liveness id for an AST node is +called a `live_node` (it's a newtype'd uint) and the id for a variable +is called a `variable` (another newtype'd uint). + +On the way back up the tree, as we are about to exit from a function +declaration we allocate a `liveness` instance. Now that we know +precisely how many nodes and variables we need, we can allocate all +the various arrays that we will need to precisely the right size. We then +perform the actual propagation on the `liveness` instance. + +This propagation is encoded in the various `propagate_through_*()` +methods. It effectively does a reverse walk of the AST; whenever we +reach a loop node, we iterate until a fixed point is reached. + +## The `users` struct + +At each live node `N`, we track three pieces of information for each +variable `V` (these are encapsulated in the `users` struct): + +- `reader`: the `live_node` ID of some node which will read the value + that `V` holds on entry to `N`. Formally: a node `M` such + that there exists a path `P` from `N` to `M` where `P` does not + write `V`. If the `reader` is `invalid_node()`, then the current + value will never be read (the variable is dead, essentially). + +- `writer`: the `live_node` ID of some node which will write the + variable `V` and which is reachable from `N`. Formally: a node `M` + such that there exists a path `P` from `N` to `M` and `M` writes + `V`. If the `writer` is `invalid_node()`, then there is no writer + of `V` that follows `N`. + +- `used`: a boolean value indicating whether `V` is *used*. We + distinguish a *read* from a *use* in that a *use* is some read that + is not just used to generate a new value. For example, `x += 1` is + a read but not a use. This is used to generate better warnings. + +## Special Variables + +We generate various special variables for various, well, special purposes. +These are described in the `specials` struct: + +- `exit_ln`: a live node that is generated to represent every 'exit' from the + function, whether it be by explicit return, fail, or other means. + +- `fallthrough_ln`: a live node that represents a fallthrough + +- `no_ret_var`: a synthetic variable that is only 'read' from, the + fallthrough node. This allows us to detect functions where we fail + to return explicitly. + +- `self_var`: a variable representing 'self' + +"]; import dvec::{dvec, extensions}; import std::map::{hashmap, int_hash, str_hash, box_str_hash};