public ExprInfo(final Expr expr, final ExprKey key) {
			this.key = key;

			prototype = (Expr) expr.clone();

			// Clean up the expression's children (remember that expr's children
			// are also cloned, so we aren't changing the tree).
			prototype.visitChildren(new TreeVisitor() {
				public void visitStoreExpr(final StoreExpr expr) {
					expr.target().setDef(null);
					expr.target().setParent(null);
					expr.replaceWith(expr.target(), false);
					expr.cleanupOnly();
					expr.expr().cleanup();
				}

				public void visitVarExpr(final VarExpr expr) {
					expr.setDef(null);
				}

				public void visitConstantExpr(final ConstantExpr expr) {
				}

				// The prototype expression should only
				// contain StoreExpr, VarExpr, or
				// ConstantExpr...
				public void visitExpr(final Expr expr) {
					throw new RuntimeException();
				}
			});

			numUses = 0;

			reals = new ArrayList[cfg.size()];
			realsSorted = new boolean[cfg.size()];

			for (int i = 0; i < reals.length; i++) {
				reals[i] = new ArrayList();
				realsSorted[i] = false;
			}

			phis = new Phi[cfg.size()];

			defs = new HashMap();
			availDefs = new HashMap();
			saves = new HashMap();
			reloads = new HashMap();

			if (prototype instanceof MemRefExpr) {
				// Traverse the tree and determine whether expr accesses a final
				// field.
				final FinalChecker fch = new FinalChecker();
				prototype.visit(fch);
				isFinal = fch.isFinal;

			} else {
				isFinal = true;
			}

			// For PRE, RCs, UCs, stores, and possible reassignment
			// through aliases are not considered side effects.
			sideEffects.reset();
			prototype.visit(sideEffects);

			int flag = sideEffects.sideEffects();
			flag &= ~SideEffectChecker.STORE;
			flag &= ~SideEffectChecker.ALIAS;
			flag &= ~SideEffectChecker.RC;
			flag &= ~SideEffectChecker.UC;
			hasSideEffects = flag != 0;

			// Special case: allow RC(S) and UC(S).
			if ((flag & SideEffectChecker.STACK) != 0) {
				Assert.isTrue(prototype instanceof CheckExpr);
				hasStackVariable = true;
			}
		}