Index: src/Common/PassVisitor.cc
===================================================================
--- src/Common/PassVisitor.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
+++ src/Common/PassVisitor.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -0,0 +1,18 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// PassVisitor.cc --
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 03 14:53:53 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include "Common/PassVisitor.h"
+
+PassVisitorStats pass_visitor_stats;
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Common/PassVisitor.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -5,4 +5,5 @@
 #include <stack>
 
+#include "Common/Stats.h"
 #include "Common/utility.h"
 
@@ -426,4 +427,12 @@
 };
 
+#include "Common/Stats.h"
+
+extern struct PassVisitorStats {
+	size_t depth = 0;
+	Stats::Counters::MaxCounter<double> * max = nullptr;
+	Stats::Counters::AverageCounter<double> * avg = nullptr;
+} pass_visitor_stats;
+
 #include "SynTree/TypeSubstitution.h"
 #include "PassVisitor.impl.h"
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Common/PassVisitor.impl.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -67,5 +67,10 @@
 	SemanticErrorException errors;
 
+	pass_visitor_stats.depth++;
+	pass_visitor_stats.max->push(pass_visitor_stats.depth);
+	pass_visitor_stats.avg->push(pass_visitor_stats.depth);
 	for ( std::list< Declaration* >::iterator i = decls.begin(); ; ++i ) {
+
+
 		// splice in new declarations after previous decl
 		if ( !empty( afterDecls ) ) { decls.splice( i, *afterDecls ); }
@@ -83,4 +88,5 @@
 		if ( !empty( beforeDecls ) ) { decls.splice( i, *beforeDecls ); }
 	}
+	pass_visitor_stats.depth--;
 	if ( ! errors.isEmpty() ) {
 		throw errors;
@@ -94,4 +100,7 @@
 	SemanticErrorException errors;
 
+	pass_visitor_stats.depth++;
+	pass_visitor_stats.max->push(pass_visitor_stats.depth);
+	pass_visitor_stats.avg->push(pass_visitor_stats.depth);
 	for ( std::list< Declaration* >::iterator i = decls.begin(); ; ++i ) {
 		// splice in new declarations after previous decl
@@ -109,4 +118,5 @@
 		if ( !empty( beforeDecls ) ) { decls.splice( i, *beforeDecls ); }
 	}
+	pass_visitor_stats.depth--;
 	if ( ! errors.isEmpty() ) {
 		throw errors;
@@ -126,4 +136,8 @@
 	if ( ! visitor.get_visit_children() ) return;
 	SemanticErrorException errors;
+
+	pass_visitor_stats.depth++;
+	pass_visitor_stats.max->push(pass_visitor_stats.depth);
+	pass_visitor_stats.avg->push(pass_visitor_stats.depth);
 	for ( typename Container::iterator i = container.begin(); i != container.end(); ++i ) {
 		try {
@@ -135,4 +149,5 @@
 		}
 	}
+	pass_visitor_stats.depth--;
 	if ( ! errors.isEmpty() ) {
 		throw errors;
@@ -153,4 +168,8 @@
 	if ( ! mutator.get_visit_children() ) return;
 	SemanticErrorException errors;
+
+	pass_visitor_stats.depth++;
+	pass_visitor_stats.max->push(pass_visitor_stats.depth);
+	pass_visitor_stats.avg->push(pass_visitor_stats.depth);
 	for ( typename Container::iterator i = container.begin(); i != container.end(); ++i ) {
 		try {
@@ -163,4 +182,5 @@
 		} // try
 	} // for
+	pass_visitor_stats.depth--;
 	if ( ! errors.isEmpty() ) {
 		throw errors;
@@ -185,4 +205,7 @@
 	DeclList_t* afterDecls  = get_afterDecls();
 
+	pass_visitor_stats.depth++;
+	pass_visitor_stats.max->push(pass_visitor_stats.depth);
+	pass_visitor_stats.avg->push(pass_visitor_stats.depth);
 	for ( std::list< Statement* >::iterator i = statements.begin(); i != statements.end(); ++i ) {
 
@@ -202,4 +225,5 @@
 		if ( !empty( beforeStmts ) ) { statements.splice( i, *beforeStmts ); }
 	}
+	pass_visitor_stats.depth--;
 
 	if ( !empty( afterDecls ) ) { splice( std::back_inserter( statements ), afterDecls); }
Index: src/Common/Stats.h
===================================================================
--- src/Common/Stats.h	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Common/Stats.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// Heap.h --
+// Stats.h --
 //
 // Author           : Thierry Delisle
@@ -16,4 +16,34 @@
 #pragma once
 
+// Entry point for compiler analytics.
+/*
+The compiler currently supports 3 times of analytics:
+	 - generic counters
+	 - heap statistics
+	 - timiing statistics
+
+These can be enabled using the --stats option, to which a comma seperated list of options can be passed.
+For more details see Stats.cc
+
+Counters:
+	The counters are a generic tree of counters that print in a 2-column output format.
+	They can count maximums, averages, totals, etc.
+
+	Currently all counters are under the same enable block, this could be changed if needed.
+
+Heap:
+	Measures the total calls malloc and free as the peak number of allocations per pass
+
+Timing:
+	Comming soon
+*/
+
+
 #include "Common/Stats/Counter.h"
 #include "Common/Stats/Heap.h"
+#include "Common/Stats/Time.h"
+
+namespace Stats {
+	void parse_params(const char * const params);
+	void print();
+}
Index: src/Common/Stats/Base.h
===================================================================
--- src/Common/Stats/Base.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
+++ src/Common/Stats/Base.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -0,0 +1,86 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Heap.h --
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 03 14:53:53 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+#include <cstdint>
+#include <iostream>
+
+namespace Stats {
+	namespace Base {
+		class TreeImpl;
+
+		struct TreeTop {
+			TreeImpl * head = nullptr;
+			TreeImpl * tail = nullptr;
+
+			inline void append(TreeImpl * node);
+		};
+
+		template<typename func_t>
+		void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy = false);
+
+		class TreeImpl {
+		public:
+			virtual void print(std::ostream &) = 0;
+
+			const char * const name;
+			TreeImpl(const char * const name) : name(name) {}
+
+		protected:
+			virtual ~TreeImpl() = default;
+
+			TreeImpl * next = nullptr;
+			TreeTop children;
+
+			friend struct TreeTop;
+
+			template<typename func_t>
+			friend void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy);
+		};
+
+		void TreeTop::append(TreeImpl * node) {
+			if(!head) { head = node; }
+			else      { tail->next = node;}
+			tail = node;
+		}
+
+		template<typename func_t>
+		inline void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy) {
+			auto it = range.head;
+			while(it) {
+				auto next = it->next;
+				func(it, level);
+				ForAll(it->children, level + 1, func);
+				if(destroy) delete it;
+				it = next;
+			}
+		}
+
+		template<TreeTop & top>
+		class Tree : public TreeImpl {
+		public:
+			Tree(const char * const name) : TreeImpl{name} {
+				top.append(this);
+			}
+
+			Tree(const char * const name, Tree * parent) : TreeImpl{name} {
+				parent->children.append(this);
+			}
+		protected:
+			virtual ~Tree() = default;
+		};
+	}
+}
Index: src/Common/Stats/Counter.cc
===================================================================
--- src/Common/Stats/Counter.cc	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Common/Stats/Counter.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// Heap.h --
+// Counter.cc --
 //
 // Author           : Thierry Delisle
@@ -18,24 +18,13 @@
 #include <algorithm>
 #include <cstring>
+#include <functional>
 #include <iomanip>
 
 namespace Stats {
 	namespace Counters {
-
-		template<typename T>
-		void ForAllCounters(BaseCounter::list_t & range, size_t level, T func) {
-			auto it = range.head;
-			while(it) {
-				auto next = it->next;
-				func(it, level);
-				ForAllCounters(it->children, level + 1, func);
-				it = next;
-			}
-		}
-
 		void print() {
-			if(!BaseCounter::top.head) return;
+			if(!top.head) return;
 			size_t nc = 0;
-			ForAllCounters(BaseCounter::top, 0, [&](BaseCounter * node, size_t level) {
+			Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
 				nc = std::max(nc, (4 * level) + std::strlen(node->name));
 			});
@@ -49,5 +38,5 @@
 
 
-			ForAllCounters(BaseCounter::top, 0, [&](BaseCounter * node, size_t level) {
+			Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
 				std::cerr << std::string(level * 4, ' ');
 				std::cerr << node->name;
@@ -58,11 +47,12 @@
 				std::cerr << " |";
 				std::cerr << '\n';
-				delete node;
-			});
+			}, true);
 
 			std::cerr << std::string(nct, '-') << std::endl;
 		}
 
-		BaseCounter::list_t BaseCounter::top;
+		Base::TreeTop top;
+
+		extern bool enabled;
 	}
 }
Index: src/Common/Stats/Counter.h
===================================================================
--- src/Common/Stats/Counter.h	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Common/Stats/Counter.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// Heap.h --
+// Counter.h --
 //
 // Author           : Thierry Delisle
@@ -19,134 +19,126 @@
 #include <iostream>
 
+#include "Common/Stats/Base.h"
+
+#if defined( NO_STATISTICS )
+	#define NO_COUNTER_STATISTICS
+#endif
+
 namespace Stats {
 	namespace Counters {
-		void print();
+# 		if defined(NO_COUNTERS_STATISTICS)
 
-		class BaseCounter {
-		public:
-			BaseCounter(const char * const name) : name(name) {
-				top.append(this);
+			static inline void print() {}
+
+			class CounterGroup {
+			public:
+			};
+
+			class SimpleCounter {
+			public:
+				inline void operator++(int) {}
+				inline void operator+=(size_t) {}
+			};
+
+			template<typename T>
+			class AverageCounter {
+			public:
+				inline void push(T value) {}
+			};
+
+			template<typename T>
+			class MaxCounter {
+			public:
+				inline void push(T value) {}
+			};
+
+			template<typename counter_t>
+			counter_t * build(const char * const name) {
+				return nullptr;
 			}
 
-			BaseCounter(const char * const name, BaseCounter * parent) : name(name) {
-				parent->children.append(this);
+			template<typename counter_t>
+			counter_t * build(const char * const name, Base::Tree<top> * parent) {
+					return nullptr;
 			}
-		protected:
-			virtual ~BaseCounter() = default;
+#		else
+			extern bool enabled;
 
-			struct list_t {
-				BaseCounter * head = nullptr;
-				BaseCounter * tail = nullptr;
+			extern Base::TreeTop top;
 
-				void append(BaseCounter * node) {
-					if(!head) { head = node; }
-					else      { tail->next = node;}
-					tail = node;
-				}
+			class CounterGroup : public Base::Tree<top> {
+			public:
+				CounterGroup(const char * const name ) : Base::Tree<top>(name) {}
+				CounterGroup(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent) {}
+
+				virtual void print(std::ostream & os) override { os << ""; }
+			protected:
+				virtual ~CounterGroup() = default;
 			};
 
-		private:
-			virtual void print(std::ostream &) = 0;
+			class SimpleCounter : public Base::Tree<top> {
+			public:
+				SimpleCounter(const char * const name ) : Base::Tree<top>(name) {}
+				SimpleCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent) {}
+
+				virtual void print(std::ostream & os) override { os << count; }
+
+				inline void operator++(int)          { if(!enabled) return; count++;        }
+				inline void operator+=(size_t value) { if(!enabled) return; count += value; }
+
+			protected:
+				virtual ~SimpleCounter() = default;
+
+			private:
+				size_t count = 0;
+			};
+
 			template<typename T>
-			friend void ForAllCounters(BaseCounter::list_t &, size_t, T );
-			friend void print();
+			class AverageCounter : public Base::Tree<top> {
+			public:
+				AverageCounter(const char * const name ) : Base::Tree<top>(name), sum{} {}
+				AverageCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent), sum{} {}
 
-		private:
-			const char * const name;
+				virtual void print(std::ostream & os) { os << sum / count; }
 
-			BaseCounter * next = nullptr;
-			list_t children;
+				inline void push(T value) { if(!enabled) return; sum += value; count++; }
 
-			static list_t top;
-		};
+			protected:
+				virtual ~AverageCounter() = default;
 
-		class CounterGroup : public BaseCounter {
-		public:
-			CounterGroup(const char * const name ) : BaseCounter(name) {}
-			CounterGroup(const char * const name, BaseCounter * parent) : BaseCounter(name, parent) {}
+			private:
+				T sum;
+				size_t count = 1;
+			};
 
-		protected:
-			virtual ~CounterGroup() = default;
+			template<typename T>
+			class MaxCounter : public Base::Tree<top> {
+			public:
+				MaxCounter(const char * const name ) : Base::Tree<top>(name), max{} {}
+				MaxCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent), max{} {}
 
-		private:
-			virtual void print(std::ostream & os) {
-				os << "";
-			}
-			template<typename T>
-			friend void ForAllCounters(BaseCounter::list_t &, size_t, T );
-			friend void print();
-		};
+				virtual void print(std::ostream & os) { os << max; }
 
-		class SimpleCounter : public BaseCounter {
-		public:
-			SimpleCounter(const char * const name ) : BaseCounter(name) {}
-			SimpleCounter(const char * const name, BaseCounter * parent) : BaseCounter(name, parent) {}
+				inline void push(T value) { if(!enabled) return; max = std::max(max, value); }
 
-			inline void operator++(int)          { count++;        }
-			inline void operator+=(size_t value) { count += value; }
-		protected:
-			virtual ~SimpleCounter() = default;
+			protected:
+				virtual ~MaxCounter() = default;
 
-		private:
-			virtual void print(std::ostream & os) {
-				os << count;
-			}
-			template<typename T>
-			friend void ForAllCounters(BaseCounter::list_t &, size_t, T );
-			friend void print();
+			private:
+				T max;
+			};
 
-			size_t count = 0;
-
-		};
-
-		template<typename T>
-		class AverageCounter : public BaseCounter {
-		public:
-			AverageCounter(const char * const name ) : BaseCounter(name), sum{} {}
-			AverageCounter(const char * const name, BaseCounter * parent) : BaseCounter(name, parent), sum{} {}
-
-			inline void push(T value) {
-				sum += value;
-				count++;
+			template<typename counter_t>
+			counter_t * build(const char * const name) {
+				if(!enabled) return nullptr;
+				return new counter_t(name);
 			}
 
-		protected:
-			virtual ~AverageCounter() = default;
-
-		private:
-			virtual void print(std::ostream & os) {
-				os << sum / count;
+			template<typename counter_t>
+			counter_t * build(const char * const name, Base::Tree<top> * parent) {
+				if(!enabled) return nullptr;
+				return new counter_t(name, parent);
 			}
-			template<typename F>
-			friend void ForAllCounters(BaseCounter::list_t &, size_t, F );
-			friend void print();
-
-			T sum;
-			size_t count = 1;
-		};
-
-		template<typename T>
-		class MaxCounter : public BaseCounter {
-		public:
-			MaxCounter(const char * const name ) : BaseCounter(name), max{} {}
-			MaxCounter(const char * const name, BaseCounter * parent) : BaseCounter(name, parent), max{} {}
-
-			inline void push(T value) {
-				max = std::max(max, value);
-			}
-
-		protected:
-			virtual ~MaxCounter() = default;
-
-		private:
-			virtual void print(std::ostream & os) {
-				os << max;
-			}
-			template<typename F>
-			friend void ForAllCounters(BaseCounter::list_t &, size_t, F );
-			friend void print();
-
-			T max;
-		};
+#		endif
 	}
 }
Index: src/Common/Stats/Heap.cc
===================================================================
--- src/Common/Stats/Heap.cc	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Common/Stats/Heap.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -21,4 +21,8 @@
 #include <iostream>
 
+#if defined( NO_STATISTICS )
+	#define NO_HEAP_STATISTICS
+#endif
+
 namespace Stats {
 	namespace Heap {
@@ -28,4 +32,6 @@
 		void print() {}
 #else
+		extern bool enabled;
+
 		struct StatBlock {
 			const char * name  = nullptr;	///< Name of this pass
@@ -77,4 +83,6 @@
 
 		void print() {
+			if(!enabled) return;
+
 			size_t nc = 0;
 			size_t total_mallocs = 0;
@@ -166,5 +174,5 @@
 			void * malloc( size_t size ) {
 				static auto __malloc = reinterpret_cast<void * (*)(size_t)>(interpose_symbol( "malloc", nullptr ));
-				if( passes_cnt > 0 ) {
+				if( enabled && passes_cnt > 0 ) {
 					passes[passes_cnt - 1].mallocs++;
 					passes[passes_cnt - 1].n_allocs++;
@@ -177,5 +185,5 @@
 			void free( void * ptr ) {
 				static auto __free = reinterpret_cast<void   (*)(void *)>(interpose_symbol( "free", nullptr ));
-				if( passes_cnt > 0 ) {
+				if( enabled && passes_cnt > 0 ) {
 					passes[passes_cnt - 1].frees++;
 					passes[passes_cnt - 1].n_allocs--;
@@ -186,5 +194,5 @@
 			void * calloc( size_t nelem, size_t size ) {
 				static auto __calloc = reinterpret_cast<void * (*)(size_t, size_t)>(interpose_symbol( "calloc", nullptr ));
-				if( passes_cnt > 0 ) {
+				if( enabled && passes_cnt > 0 ) {
 					passes[passes_cnt - 1].mallocs++;
 					passes[passes_cnt - 1].n_allocs++;
@@ -198,5 +206,5 @@
 				static auto __realloc = reinterpret_cast<void * (*)(void *, size_t)>(interpose_symbol( "realloc", nullptr ));
 				void * s = __realloc( ptr, size );
-				if ( s != ptr && passes_cnt > 0 ) {			// did realloc get new storage ?
+				if ( enabled && s != ptr && passes_cnt > 0 ) {			// did realloc get new storage ?
 					passes[passes_cnt - 1].mallocs++;
 					passes[passes_cnt - 1].frees++;
Index: src/Common/Stats/Stats.cc
===================================================================
--- src/Common/Stats/Stats.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
+++ src/Common/Stats/Stats.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -0,0 +1,86 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Stats.cc --
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 01 15:45:08 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+
+namespace Stats {
+	namespace Counters {
+		bool enabled = false;
+		void print();
+	}
+
+	namespace Heap {
+		bool enabled = false;
+		void print();
+	}
+
+	namespace Time {
+		bool enabled = false;
+		void print();
+	}
+
+	struct {
+		const char * const opt;
+		bool & enabled;
+	}
+	statistics[] = {
+		{ "counters", Counters::enabled },
+		{ "heap"    , Heap::enabled },
+		{ "time"    , Time::enabled },
+	};
+
+	void set_param(std::string & param) {
+		if(param == "all") {
+			for(auto & stat : statistics) {
+				stat.enabled = true;
+			}
+			return;
+		}
+
+		if(param == "none") {
+			for(auto & stat : statistics) {
+				stat.enabled = false;
+			}
+			return;
+		}
+
+		for(auto & stat : statistics) {
+			if(stat.opt == param) {
+				stat.enabled = true;
+				return;
+			}
+		}
+
+		std::cerr << "Ignoring unknown statistic " << param << std::endl;
+	}
+
+	void parse_params(const char * const params) {
+		std::stringstream ss(params);
+		while(ss.good()) {
+			std::string substr;
+			getline( ss, substr, ',' );
+			set_param(substr);
+		}
+	}
+
+	void print() {
+		Counters::print();
+		Heap::print();
+		Time::print();
+	}
+}
Index: src/Common/Stats/Time.cc
===================================================================
--- src/Common/Stats/Time.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
+++ src/Common/Stats/Time.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -0,0 +1,203 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Time.cc --
+//
+// Author           : Thierry Delisle
+// Created On       : Mon Mar 04 15:16:07 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include "Time.h"
+
+#include <cassert>
+#include <chrono>
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <iomanip>
+#include <stack>
+
+namespace Stats {
+	namespace Time {
+#		if !defined(NO_TIME_STATISTICS)
+			extern bool enabled;
+
+			Base::TreeTop top;
+
+			typedef  std::chrono::time_point<std::chrono::high_resolution_clock> point_t;
+			std::chrono::duration<double> total;
+
+			point_t global_begin;
+
+			size_t prevl = 0;
+			size_t currl = 0;
+
+			template<typename T>
+			static inline std::ostream & operator<<(std::ostream & os, const std::chrono::duration<T> & dd) {
+				auto d = std::chrono::duration_cast<std::chrono::milliseconds>(dd);
+				auto minutes = std::chrono::duration_cast<std::chrono::minutes>(d);
+				auto seconds = std::chrono::duration_cast<std::chrono::seconds>(d % std::chrono::minutes(1));
+				auto millis  = std::chrono::duration_cast<std::chrono::milliseconds>(d % std::chrono::seconds(1));
+
+				bool zmin = minutes == minutes.zero();
+				bool zsec = seconds == seconds.zero();
+				bool zmil = millis  == millis .zero();
+
+				if(!zmin) {
+					os << std::setw(4) << minutes.count() << "m";
+				} else {
+					os << std::string(5, ' ');
+				}
+
+				if(!zmin || !zsec) {
+					if(!zmin) os << std::setfill('0');
+					os << std::setw(2) << seconds.count() << "s";
+				} else {
+					os << std::string(3, ' ');
+				}
+				os << std::setfill(' ');
+
+				if(!zmin || !zsec || !zmil) {
+					if(!zmin || !zsec) os << std::setfill('0');
+					os << std::setw(3) << millis .count();
+				} else {
+					os << std::string(4, ' ');
+				}
+				os << std::setfill(' ');
+
+				return os;
+			}
+
+			class TimerNode : public Base::Tree<top> {
+			public:
+				TimerNode(const char * const name )
+					: Base::Tree<top>(name)
+				{}
+
+				TimerNode(const char * const name, Base::Tree<top> * parent)
+					: Base::Tree<top>(name, parent)
+
+				{}
+
+				virtual void print(std::ostream & os) override {
+					if(currl > prevl) {
+						// std::cerr << "push last " << last << std::endl;
+						parents.push(last);
+					} else if(currl < prevl) {
+						parents.pop();
+						// std::cerr << "pop, top = " << parents.top() << std::endl;
+					}
+					// else {
+						last = end - begin;
+						// std::cerr << "last = " << last << "\t";
+					// }
+
+					assert(finished);
+					std::chrono::duration<double> diff = end - begin;
+					os << diff << " | ";
+					if(parents.empty()) {
+						os << "     N/A | ";
+					} else {
+						os << std::setw(7) << std::setprecision(0);
+						os << size_t(100.0 * diff.count() / parents.top().count()) << "% | ";
+					}
+					os << std::setw(5) << std::setprecision(0);
+					os << size_t(100.0 * diff.count() / total.count()) << "% ";
+				}
+
+				void start() {
+					begin = std::chrono::high_resolution_clock::now();
+				}
+
+				void finish() {
+					end = std::chrono::high_resolution_clock::now();
+					finished = true;
+				}
+
+			protected:
+				virtual ~TimerNode() = default;
+
+			private:
+				bool finished = false;
+
+				point_t begin;
+				point_t end;
+
+				static std::chrono::duration<double> last;
+				static std::stack<std::chrono::duration<double>> parents;
+			};
+
+			std::stack<TimerNode *> nodes;
+
+			std::chrono::duration<double> TimerNode::last = {};
+			std::stack<std::chrono::duration<double>> TimerNode::parents = {};
+
+			void StartGlobal() {
+				global_begin = std::chrono::high_resolution_clock::now();
+			}
+
+			void StartBlock(const char * const name) {
+				if(!enabled) return;
+				auto node = nodes.empty()
+					? new TimerNode(name)
+					: new TimerNode(name, nodes.top());
+
+				nodes.push(node);
+				node->start();
+			}
+
+			void StopBlock() {
+				if(!enabled) return;
+				nodes.top()->finish();
+				nodes.pop();
+			}
+
+			void print() {
+				if(!top.head) return;
+				auto global_end = std::chrono::high_resolution_clock::now();
+				total = global_end - global_begin;
+
+				size_t nc = 0;
+				Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
+					nc = std::max(nc, (4 * level) + std::strlen(node->name));
+				});
+
+				size_t nct = nc + 37;
+				std::cerr << std::string(nct, '=') << std::endl;
+				const char * const title = "Timing Results";
+				std::cerr << std::string((nct - std::strlen(title)) / 2, ' ');
+				std::cerr << title << std::endl;
+				std::cerr << std::string(nct, '-') << std::endl;
+				std::cerr << "Location";
+				std::cerr << std::string(nc - (std::strlen("Location")), ' ');
+				std::cerr << " | ";
+				std::cerr << "       Time | ";
+				std::cerr << "% parent | ";
+				std::cerr << "% total |" << std::endl;
+				std::cerr << std::string(nct, '-') << std::endl;
+
+				Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
+					currl = level;
+					std::cerr << std::string(level * 4, ' ');
+					std::cerr << node->name;
+					std::cerr << std::string(nc - ((level * 4) + std::strlen(node->name)), ' ');
+					std::cerr << " | ";
+					node->print(std::cerr);
+					std::cerr << " |";
+					std::cerr << '\n';
+					prevl = level;
+				}, true);
+
+				std::cerr << std::string(nct, '-') << std::endl;
+				std::cerr << "Total " << total << std::endl;
+				std::cerr << std::string(nct, '-') << std::endl;
+			}
+#		endif
+	}
+}
Index: src/Common/Stats/Time.h
===================================================================
--- src/Common/Stats/Time.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
+++ src/Common/Stats/Time.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -0,0 +1,63 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Time.h --
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 01 15:14:11 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+#include "Common/Stats/Base.h"
+
+#if defined( NO_STATISTICS )
+	#define NO_TIME_STATISTICS
+#endif
+
+namespace Stats {
+	namespace Time {
+#		if defined(NO_TIME_STATISTICS)
+			inline void StartGlobal() {}
+
+			inline void StartBlock(const char * const) {}
+			inline void StopBlock() {}
+
+			inline void print() {}
+
+			struct BlockGuard {
+				BlockGuard(const char * const) {}
+				~BlockGuard() {}
+			};
+
+			template<typename func_t>
+			inline void TimeBlock(const char *, func_t f) {
+				f();
+			}
+#		else
+			void StartGlobal();
+
+			void StartBlock(const char * const name);
+			void StopBlock();
+
+			void print();
+
+			struct BlockGuard {
+				BlockGuard(const char * const name ) { StartBlock(name); }
+				~BlockGuard() { StopBlock(); }
+			};
+
+			template<typename func_t>
+			inline void TimeBlock(const char * name, func_t func) {
+				BlockGuard guard(name);
+				func();
+			}
+#		endif
+	}
+}
Index: src/Common/module.mk
===================================================================
--- src/Common/module.mk	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Common/module.mk	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -17,8 +17,11 @@
 SRC_COMMON = \
       Common/Assert.cc \
+      Common/Eval.cc \
+      Common/PassVisitor.cc \
+      Common/SemanticError.cc \
+      Common/Stats/Counter.cc \
       Common/Stats/Heap.cc \
-      Common/Stats/Counter.cc \
-      Common/Eval.cc \
-      Common/SemanticError.cc \
+      Common/Stats/Stats.cc \
+      Common/Stats/Time.cc \
       Common/UniqueName.cc
 
Index: src/CompilationState.cc
===================================================================
--- src/CompilationState.cc	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/CompilationState.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -38,7 +38,5 @@
 	codegenp = false,
 	prettycodegenp = false,
-	linemarks = false,
-	stats_heap = false,
-	stats_counters = false;
+	linemarks = false;
 
 // Local Variables: //
Index: src/CompilationState.h
===================================================================
--- src/CompilationState.h	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/CompilationState.h	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -39,7 +39,5 @@
 	codegenp,
 	prettycodegenp,
-	linemarks,
-	stats_heap,
-	stats_counters;
+	linemarks;
 
 // is the compiler building prelude or libcfa?
Index: src/Makefile.in
===================================================================
--- src/Makefile.in	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/Makefile.in	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -165,7 +165,9 @@
 	CodeGen/FixMain.$(OBJEXT) CodeGen/GenType.$(OBJEXT) \
 	CodeGen/OperatorTable.$(OBJEXT)
-am__objects_2 = Common/Assert.$(OBJEXT) Common/Stats/Heap.$(OBJEXT) \
-	Common/Stats/Counter.$(OBJEXT) Common/Eval.$(OBJEXT) \
-	Common/SemanticError.$(OBJEXT) Common/UniqueName.$(OBJEXT)
+am__objects_2 = Common/Assert.$(OBJEXT) Common/Eval.$(OBJEXT) \
+	Common/PassVisitor.$(OBJEXT) Common/SemanticError.$(OBJEXT) \
+	Common/Stats/Counter.$(OBJEXT) Common/Stats/Heap.$(OBJEXT) \
+	Common/Stats/Stats.$(OBJEXT) Common/Stats/Time.$(OBJEXT) \
+	Common/UniqueName.$(OBJEXT)
 am__objects_3 = ControlStruct/ForExprMutator.$(OBJEXT) \
 	ControlStruct/LabelFixer.$(OBJEXT) \
@@ -560,8 +562,11 @@
 SRC_COMMON = \
       Common/Assert.cc \
+      Common/Eval.cc \
+      Common/PassVisitor.cc \
+      Common/SemanticError.cc \
+      Common/Stats/Counter.cc \
       Common/Stats/Heap.cc \
-      Common/Stats/Counter.cc \
-      Common/Eval.cc \
-      Common/SemanticError.cc \
+      Common/Stats/Stats.cc \
+      Common/Stats/Time.cc \
       Common/UniqueName.cc
 
@@ -721,4 +726,10 @@
 Common/Assert.$(OBJEXT): Common/$(am__dirstamp) \
 	Common/$(DEPDIR)/$(am__dirstamp)
+Common/Eval.$(OBJEXT): Common/$(am__dirstamp) \
+	Common/$(DEPDIR)/$(am__dirstamp)
+Common/PassVisitor.$(OBJEXT): Common/$(am__dirstamp) \
+	Common/$(DEPDIR)/$(am__dirstamp)
+Common/SemanticError.$(OBJEXT): Common/$(am__dirstamp) \
+	Common/$(DEPDIR)/$(am__dirstamp)
 Common/Stats/$(am__dirstamp):
 	@$(MKDIR_P) Common/Stats
@@ -727,12 +738,12 @@
 	@$(MKDIR_P) Common/Stats/$(DEPDIR)
 	@: > Common/Stats/$(DEPDIR)/$(am__dirstamp)
+Common/Stats/Counter.$(OBJEXT): Common/Stats/$(am__dirstamp) \
+	Common/Stats/$(DEPDIR)/$(am__dirstamp)
 Common/Stats/Heap.$(OBJEXT): Common/Stats/$(am__dirstamp) \
 	Common/Stats/$(DEPDIR)/$(am__dirstamp)
-Common/Stats/Counter.$(OBJEXT): Common/Stats/$(am__dirstamp) \
+Common/Stats/Stats.$(OBJEXT): Common/Stats/$(am__dirstamp) \
 	Common/Stats/$(DEPDIR)/$(am__dirstamp)
-Common/Eval.$(OBJEXT): Common/$(am__dirstamp) \
-	Common/$(DEPDIR)/$(am__dirstamp)
-Common/SemanticError.$(OBJEXT): Common/$(am__dirstamp) \
-	Common/$(DEPDIR)/$(am__dirstamp)
+Common/Stats/Time.$(OBJEXT): Common/Stats/$(am__dirstamp) \
+	Common/Stats/$(DEPDIR)/$(am__dirstamp)
 Common/UniqueName.$(OBJEXT): Common/$(am__dirstamp) \
 	Common/$(DEPDIR)/$(am__dirstamp)
@@ -1115,8 +1126,11 @@
 @AMDEP_TRUE@@am__include@ @am__quote@Common/$(DEPDIR)/DebugMalloc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Common/$(DEPDIR)/Eval.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@Common/$(DEPDIR)/PassVisitor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Common/$(DEPDIR)/SemanticError.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Common/$(DEPDIR)/UniqueName.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Common/Stats/$(DEPDIR)/Counter.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Common/Stats/$(DEPDIR)/Heap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@Common/Stats/$(DEPDIR)/Stats.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@Common/Stats/$(DEPDIR)/Time.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Concurrency/$(DEPDIR)/Keywords.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Concurrency/$(DEPDIR)/Waitfor.Po@am__quote@
Index: src/SymTab/Indexer.cc
===================================================================
--- src/SymTab/Indexer.cc	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/SymTab/Indexer.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -45,14 +45,36 @@
 	// Statistics block
 	namespace {
-		auto idtable_group = new Stats::Counters::CounterGroup("IdTable");
-		auto idtable_find  = new Stats::Counters::SimpleCounter("Find calls", idtable_group);
-		auto idtable_size  = new Stats::Counters::AverageCounter<double>("Average Size", idtable_group);
-		auto idtable_key   = new Stats::Counters::AverageCounter<double>("Average Key Size", idtable_group);
-
-		auto indexers_group = new Stats::Counters::CounterGroup("Indexers");
-		auto indexers_count = new Stats::Counters::SimpleCounter("Count", indexers_group);
-		auto indexers_size  = new Stats::Counters::AverageCounter<double>("Average Size", indexers_group);
-		auto indexers_depth_a  = new Stats::Counters::AverageCounter<double>("Average Depth", indexers_group);
-		auto indexers_depth_m  = new Stats::Counters::MaxCounter<size_t>("Max Depth", indexers_group);
+
+		static inline auto stats_idtable() {
+			using namespace Stats::Counters;
+			static auto group = build<CounterGroup>("IdTable");
+			static struct {
+				SimpleCounter * find;
+				AverageCounter<double> * size;
+				AverageCounter<double> * key;
+			} ret = {
+				.find = build<SimpleCounter>("Find calls", group),
+				.size = build<AverageCounter<double>>("Average Size", group),
+				.key  = build<AverageCounter<double>>("Average Key Size", group),
+			};
+			return ret;
+		}
+
+		static inline auto stats_indexers() {
+			using namespace Stats::Counters;
+			static auto group   = build<CounterGroup>("Indexers");
+			static struct {
+				SimpleCounter * count;
+				AverageCounter<double> * size;
+				AverageCounter<double> * depth_a;
+				MaxCounter<size_t> * depth_m;
+			} ret = {
+				.count   = build<SimpleCounter>("Count", group),
+				.size    = build<AverageCounter<double>>("Average Size", group),
+				.depth_a = build<AverageCounter<double>>("Average Depth", group),
+				.depth_m = build<MaxCounter<size_t>>("Max Depth", group),
+			};
+			return ret;
+		}
 	}
 
@@ -214,9 +236,9 @@
 
 	Indexer::Indexer() : tables( 0 ), scope( 0 ) {
-		(*indexers_count)++;
+		(*stats_indexers().count)++;
 	}
 
 	Indexer::Indexer( const Indexer &that ) : doDebug( that.doDebug ), tables( newRef( that.tables ) ), scope( that.scope ) {
-		(*indexers_count)++;
+		(*stats_indexers().count)++;
 	}
 
@@ -227,11 +249,11 @@
 	Indexer::~Indexer() {
 		if(tables) {
-			indexers_size->push( tables->idTable.size() );
+			stats_indexers().size->push( tables->idTable.size() );
 			size_t depth = 1;
 			for( auto crnt = tables->base.tables; crnt; crnt = crnt->base.tables ) {
 				++depth;
 			}
-			indexers_depth_a->push( depth );
-			indexers_depth_m->push( depth );
+			stats_indexers().depth_a->push( depth );
+			stats_indexers().depth_m->push( depth );
 		}
 		deleteRef( tables );
@@ -266,7 +288,7 @@
 		while ( searchTables ) {
 
-			(*idtable_find)++;
-			idtable_key->push( id.size() );
-			idtable_size->push( searchTables->idTable.size() );
+			(*stats_idtable().find)++;
+			stats_idtable().key->push( id.size() );
+			stats_idtable().size->push( searchTables->idTable.size() );
 			IdTable::const_iterator decls = searchTables->idTable.find( id );
 			if ( decls != searchTables->idTable.end() ) {
@@ -345,7 +367,7 @@
 		if ( tables->scope < scope ) return nullptr;
 
-		(*idtable_find)++;
-		idtable_key->push( id.size() );
-		idtable_size->push( tables->idTable.size() );
+		(*stats_idtable().find)++;
+		stats_idtable().key->push( id.size() );
+		stats_idtable().size->push( tables->idTable.size() );
 		IdTable::const_iterator decls = tables->idTable.find( id );
 		if ( decls != tables->idTable.end() ) {
@@ -366,7 +388,7 @@
 		if ( tables->scope < scope ) return false;
 
-		(*idtable_find)++;
-		idtable_key->push( id.size() );
-		idtable_size->push( tables->idTable.size() );
+		(*stats_idtable().find)++;
+		stats_idtable().key->push( id.size() );
+		stats_idtable().size->push( tables->idTable.size() );
 		IdTable::const_iterator decls = tables->idTable.find( id );
 		if ( decls != tables->idTable.end() ) {
@@ -385,7 +407,7 @@
 		if ( tables->scope < scope ) return false;
 
-		(*idtable_find)++;
-		idtable_key->push( id.size() );
-		idtable_size->push( tables->idTable.size() );
+		(*stats_idtable().find)++;
+		stats_idtable().key->push( id.size() );
+		stats_idtable().size->push( tables->idTable.size() );
 		IdTable::const_iterator decls = tables->idTable.find( id );
 		if ( decls != tables->idTable.end() ) {
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/SymTab/Validate.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -304,35 +304,87 @@
 		PassVisitor<FixQualifiedTypes> fixQual;
 
-		Stats::Heap::newPass("validate-A");
-		acceptAll( translationUnit, hoistDecls );
-		ReplaceTypedef::replaceTypedef( translationUnit );
-		ReturnTypeFixer::fix( translationUnit ); // must happen before autogen
-		acceptAll( translationUnit, epc ); // must happen before VerifyCtorDtorAssign, because void return objects should not exist; before LinkReferenceToTypes because it is an indexer and needs correct types for mangling
-		Stats::Heap::newPass("validate-B");
-		acceptAll( translationUnit, lrt ); // must happen before autogen, because sized flag needs to propagate to generated functions
-		mutateAll( translationUnit, fixQual ); // must happen after LinkReferenceToTypes, because aggregate members are accessed
-		HoistStruct::hoistStruct( translationUnit ); // must happen after EliminateTypedef, so that aggregate typedefs occur in the correct order
-		EliminateTypedef::eliminateTypedef( translationUnit ); //
-		Stats::Heap::newPass("validate-C");
-		acceptAll( translationUnit, genericParams );  // check as early as possible - can't happen before LinkReferenceToTypes
-		VerifyCtorDtorAssign::verify( translationUnit );  // must happen before autogen, because autogen examines existing ctor/dtors
-		ReturnChecker::checkFunctionReturns( translationUnit );
-		InitTweak::fixReturnStatements( translationUnit ); // must happen before autogen
-		Stats::Heap::newPass("validate-D");
-		Concurrency::applyKeywords( translationUnit );
-		acceptAll( translationUnit, fpd ); // must happen before autogenerateRoutines, after Concurrency::applyKeywords because uniqueIds must be set on declaration before resolution
-		ControlStruct::hoistControlDecls( translationUnit );  // hoist initialization out of for statements; must happen before autogenerateRoutines
-		autogenerateRoutines( translationUnit ); // moved up, used to be below compoundLiteral - currently needs EnumAndPointerDecay
-		Stats::Heap::newPass("validate-E");
-		Concurrency::implementMutexFuncs( translationUnit );
-		Concurrency::implementThreadStarter( translationUnit );
-		mutateAll( translationUnit, compoundliteral );
-		ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables
-		Stats::Heap::newPass("validate-F");
-		FixObjectType::fix( translationUnit );
-		ArrayLength::computeLength( translationUnit );
-		acceptAll( translationUnit, finder ); // xxx - remove this pass soon
-		mutateAll( translationUnit, labelAddrFixer );
-		Validate::handleAttributes( translationUnit );
+		{
+			Stats::Heap::newPass("validate-A");
+			Stats::Time::BlockGuard guard("validate-A");
+			acceptAll( translationUnit, hoistDecls );
+			ReplaceTypedef::replaceTypedef( translationUnit );
+			ReturnTypeFixer::fix( translationUnit ); // must happen before autogen
+			acceptAll( translationUnit, epc ); // must happen before VerifyCtorDtorAssign, because void return objects should not exist; before LinkReferenceToTypes because it is an indexer and needs correct types for mangling
+		}
+		{
+			Stats::Heap::newPass("validate-B");
+			Stats::Time::BlockGuard guard("validate-B");
+			Stats::Time::TimeBlock("Link Reference To Types", [&]() {
+				acceptAll( translationUnit, lrt ); // must happen before autogen, because sized flag needs to propagate to generated functions
+			});
+			Stats::Time::TimeBlock("Fix Qualified Types", [&]() {
+				mutateAll( translationUnit, fixQual ); // must happen after LinkReferenceToTypes, because aggregate members are accessed
+			});
+			Stats::Time::TimeBlock("Hoist Structs", [&]() {
+				HoistStruct::hoistStruct( translationUnit ); // must happen after EliminateTypedef, so that aggregate typedefs occur in the correct order
+			});
+			Stats::Time::TimeBlock("Eliminate Typedefs", [&]() {
+				EliminateTypedef::eliminateTypedef( translationUnit ); //
+			});
+		}
+		{
+			Stats::Heap::newPass("validate-C");
+			Stats::Time::BlockGuard guard("validate-C");
+			acceptAll( translationUnit, genericParams );  // check as early as possible - can't happen before LinkReferenceToTypes
+			VerifyCtorDtorAssign::verify( translationUnit );  // must happen before autogen, because autogen examines existing ctor/dtors
+			ReturnChecker::checkFunctionReturns( translationUnit );
+			InitTweak::fixReturnStatements( translationUnit ); // must happen before autogen
+		}
+		{
+			Stats::Heap::newPass("validate-D");
+			Stats::Time::BlockGuard guard("validate-D");
+			Stats::Time::TimeBlock("Apply Concurrent Keywords", [&]() {
+				Concurrency::applyKeywords( translationUnit );
+			});
+			Stats::Time::TimeBlock("Forall Pointer Decay", [&]() {
+				acceptAll( translationUnit, fpd ); // must happen before autogenerateRoutines, after Concurrency::applyKeywords because uniqueIds must be set on declaration before resolution
+			});
+			Stats::Time::TimeBlock("Hoist Control Declarations", [&]() {
+				ControlStruct::hoistControlDecls( translationUnit );  // hoist initialization out of for statements; must happen before autogenerateRoutines
+			});
+			Stats::Time::TimeBlock("Generate Autogen routines", [&]() {
+				autogenerateRoutines( translationUnit ); // moved up, used to be below compoundLiteral - currently needs EnumAndPointerDecay
+			});
+		}
+		{
+			Stats::Heap::newPass("validate-E");
+			Stats::Time::BlockGuard guard("validate-E");
+			Stats::Time::TimeBlock("Implement Mutex Func", [&]() {
+				Concurrency::implementMutexFuncs( translationUnit );
+			});
+			Stats::Time::TimeBlock("Implement Thread Start", [&]() {
+				Concurrency::implementThreadStarter( translationUnit );
+			});
+			Stats::Time::TimeBlock("Compound Literal", [&]() {
+				mutateAll( translationUnit, compoundliteral );
+			});
+			Stats::Time::TimeBlock("Resolve With Expressions", [&]() {
+				ResolvExpr::resolveWithExprs( translationUnit ); // must happen before FixObjectType because user-code is resolved and may contain with variables
+			});
+		}
+		{
+			Stats::Heap::newPass("validate-F");
+			Stats::Time::BlockGuard guard("validate-F");
+			Stats::Time::TimeBlock("Fix Object Type", [&]() {
+				FixObjectType::fix( translationUnit );
+			});
+			Stats::Time::TimeBlock("Array Length", [&]() {
+				ArrayLength::computeLength( translationUnit );
+			});
+			Stats::Time::TimeBlock("Find Special Declarations", [&]() {
+				acceptAll( translationUnit, finder ); // xxx - remove this pass soon
+			});
+			Stats::Time::TimeBlock("Fix Label Address", [&]() {
+				mutateAll( translationUnit, labelAddrFixer );
+			});
+			Stats::Time::TimeBlock("Handle Attributes", [&]() {
+				Validate::handleAttributes( translationUnit );
+			});
+		}
 	}
 
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 34737de740e2e26a5572e02ff18b44f650d8cae8)
+++ src/main.cc	(revision b38433b20b7515fd3d549e3e5fb1cccca43a77a6)
@@ -65,8 +65,21 @@
 using namespace std;
 
-#define PASS(name, pass)                   \
+
+void NewPass(const char * const name) {
+	Stats::Heap::newPass(name);
+	using namespace Stats::Counters;
+	static auto pass_visitor_group = build<CounterGroup>("Pass Visitor");
+	auto pass = build<CounterGroup>(name, pass_visitor_group);
+	pass_visitor_stats.depth = 0;
+	pass_visitor_stats.avg = build<AverageCounter<double>>("Average Depth", pass);
+	pass_visitor_stats.max = build<MaxCounter<double>>("Max Depth", pass);
+}
+
+#define PASS(name, pass)                  \
 	if ( errorp ) { cerr << name << endl; } \
-	Stats::Heap::newPass(name);               \
-	pass;
+	NewPass(name);                          \
+	Stats::Time::StartBlock(name);          \
+	pass;                                   \
+	Stats::Time::StopBlock();
 
 LinkageSpec::Spec linkage = LinkageSpec::Cforall;
@@ -142,5 +155,5 @@
 	backtrace( 6 );										// skip first 6 stack frames
 	signal( SIGABRT, SIG_DFL);							// reset default signal handler
-    raise( SIGABRT );									// reraise SIGABRT
+		raise( SIGABRT );									// reraise SIGABRT
 } // sigAbortHandler
 
@@ -181,4 +194,8 @@
 		} // if
 
+		Stats::Time::StartGlobal();
+		NewPass("Parse");
+		Stats::Time::StartBlock("Parse");
+
 		// read in the builtins, extras, and the prelude
 		if ( ! nopreludep ) {							// include gcc builtins
@@ -231,7 +248,8 @@
 		// works okay for now.
 		CodeTools::fillLocations( translationUnit );
+		Stats::Time::StopBlock();
 
 		// add the assignment statement after the initialization of a type parameter
-		PASS( "validate", SymTab::validate( translationUnit, symtabp ) );
+		PASS( "Validate", SymTab::validate( translationUnit, symtabp ) );
 		if ( symtabp ) {
 			deleteAll( translationUnit );
@@ -250,8 +268,8 @@
 		} // if
 
-		PASS( "fixLabels", ControlStruct::fixLabels( translationUnit ) );
-		PASS( "fixNames", CodeGen::fixNames( translationUnit ) );
-		PASS( "genInit", InitTweak::genInit( translationUnit ) );
-		PASS( "expandMemberTuples" , Tuples::expandMemberTuples( translationUnit ) );
+		PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) );
+		PASS( "Fix Names", CodeGen::fixNames( translationUnit ) );
+		PASS( "Gen Init", InitTweak::genInit( translationUnit ) );
+		PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( translationUnit ) );
 		if ( libcfap ) {
 			// generate the bodies of cfa library functions
@@ -277,5 +295,5 @@
 		}
 
-		PASS( "resolve", ResolvExpr::resolve( translationUnit ) );
+		PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
 		if ( exprp ) {
 			dump( translationUnit );
@@ -284,5 +302,5 @@
 
 		// fix ObjectDecl - replaces ConstructorInit nodes
-		PASS( "fixInit", InitTweak::fix( translationUnit, buildingLibrary() ) );
+		PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) );
 		if ( ctorinitp ) {
 			dump ( translationUnit );
@@ -290,13 +308,13 @@
 		} // if
 
-		PASS( "expandUniqueExpr", Tuples::expandUniqueExpr( translationUnit ) ); // xxx - is this the right place for this? want to expand ASAP so tha, sequent passes don't need to worry about double-visiting a unique expr - needs to go after InitTweak::fix so that copy constructed return declarations are reused
-
-		PASS( "translateEHM" , ControlStruct::translateEHM( translationUnit ) );
-
-		PASS( "generateWaitfor" , Concurrency::generateWaitFor( translationUnit ) );
-
-		PASS( "convertSpecializations",  GenPoly::convertSpecializations( translationUnit ) ); // needs to happen before tuple types are expanded
-
-		PASS( "expandTuples", Tuples::expandTuples( translationUnit ) ); // xxx - is this the right place for this?
+		PASS( "Expand Unique Expr", Tuples::expandUniqueExpr( translationUnit ) ); // xxx - is this the right place for this? want to expand ASAP so tha, sequent passes don't need to worry about double-visiting a unique expr - needs to go after InitTweak::fix so that copy constructed return declarations are reused
+
+		PASS( "Translate EHM" , ControlStruct::translateEHM( translationUnit ) );
+
+		PASS( "Gen Waitfor" , Concurrency::generateWaitFor( translationUnit ) );
+
+		PASS( "Convert Specializations",  GenPoly::convertSpecializations( translationUnit ) ); // needs to happen before tuple types are expanded
+
+		PASS( "Expand Tuples", Tuples::expandTuples( translationUnit ) ); // xxx - is this the right place for this?
 
 		if ( tuplep ) {
@@ -305,12 +323,12 @@
 		}
 
-		PASS( "virtual expandCasts", Virtual::expandCasts( translationUnit ) ); // Must come after translateEHM
-
-		PASS( "instantiateGenerics", GenPoly::instantiateGeneric( translationUnit ) );
+		PASS( "Virtual Expand Casts", Virtual::expandCasts( translationUnit ) ); // Must come after translateEHM
+
+		PASS( "Instantiate Generics", GenPoly::instantiateGeneric( translationUnit ) );
 		if ( genericsp ) {
 			dump( translationUnit );
 			return 0;
 		}
-		PASS( "convertLvalue", GenPoly::convertLvalue( translationUnit ) );
+		PASS( "Convert L-Value", GenPoly::convertLvalue( translationUnit ) );
 
 
@@ -319,5 +337,5 @@
 			return 0;
 		} // if
-		PASS( "box", GenPoly::box( translationUnit ) );
+		PASS( "Box", GenPoly::box( translationUnit ) );
 
 		if ( bcodegenp ) {
@@ -331,5 +349,5 @@
 
 		CodeTools::fillLocations( translationUnit );
-		PASS( "codegen", CodeGen::generate( translationUnit, *output, ! noprotop, prettycodegenp, true, linemarks ) );
+		PASS( "Code Gen", CodeGen::generate( translationUnit, *output, ! noprotop, prettycodegenp, true, linemarks ) );
 
 		CodeGen::FixMain::fix( *output, (PreludeDirector + "/bootloader.c").c_str() );
@@ -377,8 +395,5 @@
 
 	deleteAll( translationUnit );
-	if(!libcfap && !treep) {
-		if(stats_counters) Stats::Counters::print();
-		if(stats_heap) Stats::Heap::print();
-	}
+	Stats::print();
 
 	return 0;
@@ -421,117 +436,100 @@
 	while ( (c = getopt_long( argc, argv, "abBcCdefgGlLmnNpqrRstTvwW:yzZD:F:", long_opts, &long_index )) != -1 ) {
 		switch ( c ) {
-		  case Ast:
-		  case 'a':										// dump AST
+			case Ast:
+			case 'a':										// dump AST
 			astp = true;
 			break;
-		  case Bresolver:
-		  case 'b':										// print before resolver steps
+			case Bresolver:
+			case 'b':										// print before resolver steps
 			bresolvep = true;
 			break;
-		  case 'B':										// print before box steps
+			case 'B':										// print before box steps
 			bboxp = true;
 			break;
-		  case CtorInitFix:
-		  case 'c':										// print after constructors and destructors are replaced
+			case CtorInitFix:
+			case 'c':										// print after constructors and destructors are replaced
 			ctorinitp = true;
 			break;
-		  case 'C':										// print before code generation
+			case 'C':										// print before code generation
 			bcodegenp = true;
 			break;
-		  case DeclStats:
-		  case 'd':
-		    declstatsp = true;
-			break;
-		  case Expr:
-		  case 'e':										// dump AST after expression analysis
+			case DeclStats:
+			case 'd':
+				declstatsp = true;
+			break;
+			case Expr:
+			case 'e':										// dump AST after expression analysis
 			exprp = true;
 			break;
-		  case ExprAlt:
-		  case 'f':										// print alternatives for expressions
+			case ExprAlt:
+			case 'f':										// print alternatives for expressions
 			expraltp = true;
 			break;
-		  case Grammar:
-		  case 'g':										// bison debugging info (grammar rules)
+			case Grammar:
+			case 'g':										// bison debugging info (grammar rules)
 			yydebug = true;
 			break;
-		  case 'G':										// dump AST after instantiate generics
+			case 'G':										// dump AST after instantiate generics
 			genericsp = true;
 			break;
-		  case LibCFA:
-		  case 'l':										// generate libcfa.c
+			case LibCFA:
+			case 'l':										// generate libcfa.c
 			libcfap = true;
 			break;
-		  case Linemarks:
-		  case 'L':										// print lines marks
+			case Linemarks:
+			case 'L':										// print lines marks
 			linemarks = true;
 			break;
-		  case Nopreamble:
-		  case 'n':										// do not read preamble
+			case Nopreamble:
+			case 'n':										// do not read preamble
 			nopreludep = true;
 			break;
-		  case Nolinemarks:
-		  case 'N':										// suppress line marks
+			case Nolinemarks:
+			case 'N':										// suppress line marks
 			linemarks = false;
 			break;
-		  case Prototypes:
-		  case 'p':										// generate prototypes for preamble functions
+			case Prototypes:
+			case 'p':										// generate prototypes for preamble functions
 			noprotop = true;
 			break;
-		  case PreludeDir:
-		  	PreludeDirector = optarg;
-			break;
-		  case 'm':										// don't replace the main
-		  	nomainp = true;
-			break;
-		  case Parse:
-		  case 'q':										// dump parse tree
+			case PreludeDir:
+				PreludeDirector = optarg;
+			break;
+			case 'm':										// don't replace the main
+				nomainp = true;
+			break;
+			case Parse:
+			case 'q':										// dump parse tree
 			parsep = true;
 			break;
-		  case Resolver:
-		  case 'r':										// print resolver steps
+			case Resolver:
+			case 'r':										// print resolver steps
 			resolvep = true;
 			break;
-		  case 'R':										// dump resolv-proto instance
+			case 'R':										// dump resolv-proto instance
 			resolvprotop = true;
 			break;
-		  case Stats:
-			{
-				std::stringstream ss(optarg);
-				while(ss.good()) {
-					std::string substr;
-					getline( ss, substr, ',' );
-					if(substr == "counters") {
-						stats_counters = true;
-					} else if(substr == "heap") {
-						stats_heap = true;
-					} else if(substr == "none") {
-						stats_counters = false;
-						stats_heap = false;
-					} else {
-						std::cerr << "Ignoring unknown statistic " << substr << std::endl;
-					}
-				}
-
-			}
-			break;
-		  case Symbol:
-		  case 's':										// print symbol table events
+			case Stats:
+				Stats::parse_params(optarg);
+			break;
+			case Symbol:
+			case 's':										// print symbol table events
 			symtabp = true;
 			break;
-		  case Tree:
-		  case 't':										// build in tree
+			case Tree:
+			case 't':										// build in tree
 			treep = true;
 			break;
-		  case TupleExpansion:
-		  case 'T':										// print after tuple expansion
+			case TupleExpansion:
+			case 'T':										// print after tuple expansion
 			tuplep = true;
 			break;
-		  case 'v':										// dump AST after decl validation pass
+			case 'v':										// dump AST after decl validation pass
 			validp = true;
 			break;
-		  case 'w':
+			case 'w':
 			Wsuppress = true;
 			break;
-		  case 'W':
+			case 'W':
 			if ( strcmp( optarg, "all" ) == 0 ) {
 				SemanticWarning_EnableAll();
@@ -550,8 +548,8 @@
 			} // if
 			break;
-		  case 'y':										// dump AST on error
+			case 'y':										// dump AST on error
 			errorp = true;
 			break;
-		  case 'z':										// dump as codegen rather than AST
+			case 'z':										// dump as codegen rather than AST
 			codegenp = true;
 			break;
@@ -559,10 +557,10 @@
 			prettycodegenp = true;
 			break;
-		  case 'D':										// ignore -Dxxx
-			break;
-		  case 'F':										// source file-name without suffix
+			case 'D':										// ignore -Dxxx
+			break;
+			case 'F':										// source file-name without suffix
 			filename = optarg;
 			break;
-		  case '?':
+			case '?':
 			if ( optopt ) {								// short option ?
 				assertf( false, "Unknown option: -%c\n", (char)optopt );
@@ -573,5 +571,5 @@
 				__attribute__((fallthrough));
 			#endif
-		  default:
+			default:
 			abort();
 		} // switch
