Index: src/CodeTools/DeclStats.cc
===================================================================
--- src/CodeTools/DeclStats.cc	(revision 41a7137de5c42b8cb1bc898ffbadea8141f9c410)
+++ src/CodeTools/DeclStats.cc	(revision fa2de9578cda12bc9dfa0f3bc8f2c87a3754e630)
@@ -17,9 +17,152 @@
 
 #include <iostream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "Common/VectorMap.h"
+#include "Parser/LinkageSpec.h"
+#include "SynTree/Declaration.h"
+#include "SynTree/Visitor.h"
 
 namespace CodeTools {
+	
+	class DeclStats : public Visitor {
+		struct Stats {
+			unsigned n_decls;     ///< Total number of declarations
+			unsigned mono_decls;  ///< Monomorphic declarations
+			unsigned poly_decls;  ///< Polymorphic declarations
+			/// Count of declarations with each name
+			std::unordered_map<std::string, unsigned> by_name;
+			/// Count of declarations with each number of parameters
+			VectorMap<unsigned> by_params;
+			/// Count of declarations with each number of return types
+			VectorMap<unsigned> by_returns;
+
+			Stats() : n_decls(0), mono_decls(0), poly_decls(0), by_name() {}
+
+			Stats& operator+= (const Stats& o) {
+				n_decls += o.n_decls;
+				mono_decls += o.mono_decls;
+				poly_decls += o.poly_decls;
+				
+				for ( auto& entry : o.by_name ) {
+					by_name[ entry.first ] += entry.second;
+				}
+				
+				by_params.reserve( o.by_params.size() );
+				for ( unsigned i = 0; i < o.by_params.size(); ++i ) {
+					by_params[i] += o.by_params[i];
+				}
+
+				by_returns.reserve( o.by_returns.size() );
+				for ( unsigned i = 0; i < o.by_returns.size(); ++i ) {
+					by_returns[i] += o.by_returns[i];
+				}
+
+				return *this;
+			}
+		};
+
+		Stats for_linkage[LinkageSpec::NoOfSpecs];   ///< Stores separate stats per linkage
+		std::unordered_set<std::string> seen_names;  ///< Stores manglenames already seen to avoid double-counting
+		Stats total;
+
+	public:
+		using Visitor::visit;
+
+		virtual void visit( FunctionDecl *decl ) {
+			// skip if already seen declaration for this function
+			const std::string& mangleName = decl->get_mangleName().empty() ? decl->get_name() : decl->get_mangleName();
+			if ( ! seen_names.insert( mangleName ).second ) return;
+			
+			Stats& stats = for_linkage[ decl->get_linkage() ];
+
+			++stats.n_decls;
+			FunctionType* fnTy = decl->get_functionType();
+			if ( fnTy->get_forall().empty() ) ++stats.mono_decls; else ++stats.poly_decls;
+
+			++stats.by_name[ decl->get_name() ];
+
+			unsigned n_params = 0;
+			for ( auto pdecl : fnTy->get_parameters() ) { n_params += pdecl->get_type()->size(); }
+			++stats.by_params.at( n_params );
+
+			unsigned n_returns = 0;
+			for ( auto rdecl : fnTy->get_returnVals() ) { n_returns += rdecl->get_type()->size(); }
+			++stats.by_returns.at( n_returns );
+		}
+
+	private:
+		template<typename F>
+		void printAll( const char* name, F extract ) {
+			std::cout << "\"" << name << "\",";
+			for ( const auto& stats : for_linkage ) {
+				std::cout << "," << extract(stats);
+			}
+			std::cout << "," << extract(total) << std::endl;
+		}
+
+		template<typename F>
+		void printAllMap( const char* name, F extract ) {
+			for ( const auto& entry : extract(total) ) {
+				const auto& key = entry.first;
+				std::cout << "\"" << name << "\"," << key;
+				for ( const auto& stats : for_linkage ) {
+					const auto& map = extract(stats);
+					auto it = map.find( key );
+					if ( it == map.end() ) std::cout << ",0";
+					else std::cout << "," << it->second;
+				}
+				std::cout  << "," << entry.second << std::endl;
+			}
+		}
+
+		template<typename F>
+		void printAllHisto( const char* name, F extract ) {
+			VectorMap<unsigned> histos[LinkageSpec::NoOfSpecs];
+			VectorMap<unsigned> thisto;
+
+			for ( const auto& entry : extract(total) ) { ++thisto.at( entry.second ); }
+
+			for ( unsigned i = 0; i < LinkageSpec::NoOfSpecs; ++i ) {
+				// can't be a higher count in one of the sub-histograms than the total
+				histos[i].reserve( thisto.size() );
+
+				for ( const auto& entry : extract(for_linkage[i]) ) { ++histos[i][entry.second]; }
+			}
+
+			for ( unsigned i = 0; i < thisto.size(); ++i ) {
+				std::cout << "\"" << name << "\"," << i;
+				for ( const auto& histo : histos ) {
+					std::cout << "," << histo[i];
+				}
+				std::cout << "," << thisto[i] << std::endl;
+			}
+		}
+		
+	public:
+		void print() {
+			for ( auto& stats : for_linkage ) {
+				total += stats;
+			}
+
+			std::cout << ",,\"intrinsic\",\"Cforall\",\"C\",\"autogen\",\"builtin\",\"TOTAL\"" << std::endl;
+
+			printAll("mono_decls", [](const Stats& stats) { return stats.mono_decls; });
+			printAll("poly_decls", [](const Stats& stats) { return stats.poly_decls; });
+			printAll("n_decls", [](const Stats& stats) { return stats.n_decls; });
+			printAll("unique_names", [](const Stats& stats) { return stats.by_name.size(); });
+
+			printAllHisto("overloads", [](const Stats& stats) { return stats.by_name; });
+			printAllMap("n_params", [](const Stats& stats) { return stats.by_params; });
+			printAllMap("n_returns", [](const Stats& stats) { return stats.by_returns; });
+		}
+	};
 
 	void printDeclStats( std::list< Declaration * > &translationUnit ) {
-		std::cout << "DeclStats not yet implemented." << std::endl;
+		DeclStats stats;
+		acceptAll( translationUnit, stats );
+		stats.print();
 	}
 	
Index: src/CodeTools/DeclStats.h
===================================================================
--- src/CodeTools/DeclStats.h	(revision 41a7137de5c42b8cb1bc898ffbadea8141f9c410)
+++ src/CodeTools/DeclStats.h	(revision fa2de9578cda12bc9dfa0f3bc8f2c87a3754e630)
@@ -21,4 +21,5 @@
 namespace CodeTools {
 
+	/// Prints summary information about a translation unit's function declarations and calls
 	void printDeclStats( std::list< Declaration * > &translationUnit );
 	
Index: src/Common/ScopedMap.h
===================================================================
--- src/Common/ScopedMap.h	(revision 41a7137de5c42b8cb1bc898ffbadea8141f9c410)
+++ src/Common/ScopedMap.h	(revision fa2de9578cda12bc9dfa0f3bc8f2c87a3754e630)
@@ -102,8 +102,8 @@
 		iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
 
-		bool operator== (const iterator &that) {
+		bool operator== (const iterator &that) const {
 			return scopes == that.scopes && level == that.level && it == that.it;
 		}
-		bool operator!= (const iterator &that) { return !( *this == that ); }
+		bool operator!= (const iterator &that) const { return !( *this == that ); }
 
 		size_type get_level() const { return level; }
@@ -180,8 +180,8 @@
 		const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
 
-		bool operator== (const const_iterator &that) {
+		bool operator== (const const_iterator &that) const {
 			return scopes == that.scopes && level == that.level && it == that.it;
 		}
-		bool operator!= (const const_iterator &that) { return !( *this == that ); }
+		bool operator!= (const const_iterator &that) const { return !( *this == that ); }
 
 		size_type get_level() const { return level; }
Index: src/Common/VectorMap.h
===================================================================
--- src/Common/VectorMap.h	(revision fa2de9578cda12bc9dfa0f3bc8f2c87a3754e630)
+++ src/Common/VectorMap.h	(revision fa2de9578cda12bc9dfa0f3bc8f2c87a3754e630)
@@ -0,0 +1,171 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ScopedMap.h --
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed Feb  1 16:55:00 2017
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Wed Feb  1 16:55:00 2017
+// Update Count     : 1
+//
+
+#ifndef _VECTORMAP_H
+#define _VECTORMAP_H
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+/// Maps integers from a contiguous range to T's
+template<typename T>
+class VectorMap {
+	std::vector<T> data;
+
+public:
+	typedef typename std::vector<T>::size_type size_type;
+	typedef size_type key_type;
+	typedef T mapped_type;
+	typedef std::pair<size_type, T&> value_type;
+	typedef std::pair<size_type, const T&> const_value_type;
+	typedef typename std::vector<T>::difference_type difference_type;
+	typedef const value_type& reference;
+	typedef const const_value_type& const_reference;
+	typedef const value_type* pointer;
+	typedef const const_value_type* const_pointer;
+	
+	class iterator : public std::iterator< std::bidirectional_iterator_tag,
+	                                       value_type,
+										    difference_type,
+											pointer,
+											reference > {
+	friend class VectorMap;
+	friend class const_iterator;
+	
+		value_type data;
+
+		iterator(size_type i, std::vector<T>& v) : data(i, v[i]) {}
+	public:
+		iterator(const iterator& that) : data(that.data) {}
+		iterator& operator= (const iterator& that) {
+			new(&data) value_type{ that.data };
+			return *this;
+		}
+
+		reference operator* () { return data; }
+		pointer operator-> () { return &data; }
+
+		iterator& operator++ () {
+			// SHENANIGANS: relies on pair<unsigned, T&> having a trivial destructor and
+			// vector<T> having contiguous layout
+			new(&data) value_type{ (data.first + 1), *(&data.second + 1) };
+			return *this;
+		}
+		iterator operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
+
+		iterator& operator-- () {
+			// SHENANIGANS: same reasons as operator++
+			new(&data) value_type{ (data.first - 1), *(&data.second - 1) };
+			return *this;
+		}
+		iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
+
+		bool operator== (const iterator& o) const {
+			return data.first == o.data.first && &data.second == &o.data.second;
+		}
+		bool operator!= (const iterator& that) const { return !(*this == that); }
+	};
+
+	class const_iterator : public std::iterator< std::bidirectional_iterator_tag,
+	                                             const_value_type,
+												  difference_type,
+												  const_pointer,
+												  const_reference  > {
+	friend class VectorMap;
+		const_value_type data;
+
+		const_iterator(size_type i, const std::vector<T>& v) : data(i, v[i]) {}
+	public:
+		const_iterator(const iterator& that) : data(that.data) {}
+		const_iterator(const const_iterator& that) : data(that.data) {}
+		const_iterator& operator= (const iterator& that) {
+			new(&data) const_value_type{ that.data };
+			return *this;
+		}
+		const_iterator& operator= (const const_iterator& that) {
+			new(&data) const_value_type{ that.data };
+			return *this;
+		}
+
+		const_reference operator* () { return data; }
+		const_pointer operator-> () { return &data; }
+
+		const_iterator& operator++ () {
+			// SHENANIGANS: same reasons as iterator::operator++
+			new(&data) const_value_type{ (data.first + 1), *(&data.second + 1) };
+			return *this;
+		}
+		const_iterator operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
+
+		const_iterator& operator-- () {
+			// SHENANIGANS: same reasons as iterator::operator++
+			new(&data) const_value_type{ (data.first - 1), *(&data.second - 1) };
+			return *this;
+		}
+		const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
+
+		bool operator== (const const_iterator& o) const {
+			return data.first == o.data.first && &data.second == &o.data.second;
+		}
+		bool operator!= (const const_iterator& that) const { return !(*this == that); }
+	};
+
+	/// Reserve space for n elements
+	void reserve(size_type n) {
+		if ( n > data.size() ) { data.insert( data.end(), n - data.size(), T{} ); }
+	}
+
+	/// Unsafe access; no bounds checking
+	T& operator[] (size_type i) { return data[i]; }
+	const T& operator[] (size_type i) const { return data[i]; }
+
+	/// Safe access; will insert new values if needed
+	T& at(size_type i) {
+		reserve(i+1);
+		return data[i];
+	}
+
+	/// Number of stored values
+	unsigned size() const { return data.size(); }
+
+	/// No stored values
+	bool empty() const { return data.empty(); }
+
+	/// Empties the map
+	void clear() { data.clear(); }
+
+	/// Returns 1 if element in map, 0 otherwise
+	size_type count( size_type i ) const { return i < size() ? 1 : 0; }
+
+	iterator begin() { return iterator{ 0, data }; }
+	const_iterator begin() const { return const_iterator{ 0, data }; }
+	const_iterator cbegin() const { return const_iterator{ 0, data }; }
+
+	iterator end() { return iterator{ data.size(), data }; }
+	const_iterator end() const { return const_iterator{ data.size(), data }; }
+	const_iterator cend() const { return const_iterator{ data.size(), data }; }
+
+	iterator find( size_type i ) { return i < size() ? iterator{ i, data } : end(); }
+	const_iterator find( size_type i ) const { return i < size() ? const_iterator{ i, data } : end(); }
+};
+
+#endif // _VECTORMAP_H
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 41a7137de5c42b8cb1bc898ffbadea8141f9c410)
+++ src/main.cc	(revision fa2de9578cda12bc9dfa0f3bc8f2c87a3754e630)
@@ -214,10 +214,4 @@
 		} // if
 
-		if ( declstatsp ) {
-			CodeTools::printDeclStats( translationUnit );
-			deleteAll( translationUnit );
-			return 0;
-		}
-
 		// add the assignment statement after the initialization of a type parameter
 		OPTPRINT( "validate" )
@@ -251,4 +245,10 @@
 			LibCfa::makeLibCfa( translationUnit );
 		} // if
+
+		if ( declstatsp ) {
+			CodeTools::printDeclStats( translationUnit );
+			deleteAll( translationUnit );
+			return 0;
+		}
 
 		if ( bresolvep ) {
