Index: libcfa/src/concurrency/kernel.cfa
===================================================================
--- libcfa/src/concurrency/kernel.cfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ libcfa/src/concurrency/kernel.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -280,5 +280,5 @@
 
 				// Spin a little on I/O, just in case
-					for(5) {
+				for(5) {
 					__maybe_io_drain( this );
 					readyThread = pop_fast( this->cltr );
@@ -287,5 +287,5 @@
 
 				// no luck, try stealing a few times
-					for(5) {
+				for(5) {
 					if( __maybe_io_drain( this ) ) {
 						readyThread = pop_fast( this->cltr );
Index: libcfa/src/concurrency/kernel.hfa
===================================================================
--- libcfa/src/concurrency/kernel.hfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ libcfa/src/concurrency/kernel.hfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -66,4 +66,5 @@
 		unsigned id;
 		unsigned target;
+		unsigned last;
 		unsigned long long int cutoff;
 	} rdq;
Index: libcfa/src/concurrency/kernel/startup.cfa
===================================================================
--- libcfa/src/concurrency/kernel/startup.cfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ libcfa/src/concurrency/kernel/startup.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -541,4 +541,5 @@
 	this.rdq.id  = -1u;
 	this.rdq.target = -1u;
+	this.rdq.last = -1u;
 	this.rdq.cutoff = 0ull;
 	do_terminate = false;
Index: libcfa/src/concurrency/ready_queue.cfa
===================================================================
--- libcfa/src/concurrency/ready_queue.cfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ libcfa/src/concurrency/ready_queue.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -24,4 +24,5 @@
 
 #include "bits/defs.hfa"
+#include "device/cpu.hfa"
 #include "kernel_private.hfa"
 
@@ -47,5 +48,7 @@
 #endif
 
-#if   defined(USE_RELAXED_FIFO)
+#if   defined(USE_CPU_WORK_STEALING)
+	#define READYQ_SHARD_FACTOR 2
+#elif defined(USE_RELAXED_FIFO)
 	#define BIAS 4
 	#define READYQ_SHARD_FACTOR 4
@@ -215,11 +218,25 @@
 //=======================================================================
 void ?{}(__ready_queue_t & this) with (this) {
-	lanes.data  = 0p;
-	lanes.tscs  = 0p;
-	lanes.count = 0;
+	#if defined(USE_CPU_WORK_STEALING)
+		lanes.count = cpu_info.hthrd_count * READYQ_SHARD_FACTOR;
+		lanes.data = alloc( lanes.count );
+		lanes.tscs = alloc( lanes.count );
+
+		for( idx; (size_t)lanes.count ) {
+			(lanes.data[idx]){};
+			lanes.tscs[idx].tv = rdtscl();
+		}
+	#else
+		lanes.data  = 0p;
+		lanes.tscs  = 0p;
+		lanes.count = 0;
+	#endif
 }
 
 void ^?{}(__ready_queue_t & this) with (this) {
-	verify( SEQUENTIAL_SHARD == lanes.count );
+	#if !defined(USE_CPU_WORK_STEALING)
+		verify( SEQUENTIAL_SHARD == lanes.count );
+	#endif
+
 	free(lanes.data);
 	free(lanes.tscs);
@@ -227,4 +244,118 @@
 
 //-----------------------------------------------------------------------
+#if defined(USE_CPU_WORK_STEALING)
+	__attribute__((hot)) void push(struct cluster * cltr, struct $thread * thrd, bool push_local) with (cltr->ready_queue) {
+		__cfadbg_print_safe(ready_queue, "Kernel : Pushing %p on cluster %p\n", thrd, cltr);
+
+		processor * const proc = kernelTLS().this_processor;
+		const bool external = !push_local || (!proc) || (cltr != proc->cltr);
+
+		const int cpu = __kernel_getcpu();
+		/* paranoid */ verify(cpu >= 0);
+		/* paranoid */ verify(cpu < cpu_info.hthrd_count);
+		/* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count);
+
+		const cpu_map_entry_t & map = cpu_info.llc_map[cpu];
+		/* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count);
+		/* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count);
+		/* paranoid */ verifyf((map.start + map.count) * READYQ_SHARD_FACTOR <= lanes.count, "have %u lanes but map can go up to %u", lanes.count, (map.start + map.count) * READYQ_SHARD_FACTOR);
+
+		const int start = map.self * READYQ_SHARD_FACTOR;
+		unsigned i;
+		do {
+			unsigned r;
+			if(unlikely(external)) { r = __tls_rand(); }
+			else { r = proc->rdq.its++; }
+			i = start + (r % READYQ_SHARD_FACTOR);
+			// If we can't lock it retry
+		} while( !__atomic_try_acquire( &lanes.data[i].lock ) );
+
+		// Actually push it
+		push(lanes.data[i], thrd);
+
+		// Unlock and return
+		__atomic_unlock( &lanes.data[i].lock );
+
+		#if !defined(__CFA_NO_STATISTICS__)
+			if(unlikely(external)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED);
+			else __tls_stats()->ready.push.local.success++;
+		#endif
+
+		__cfadbg_print_safe(ready_queue, "Kernel : Pushed %p on cluster %p (idx: %u, mask %llu, first %d)\n", thrd, cltr, i, used.mask[0], lane_first);
+
+	}
+
+	// Pop from the ready queue from a given cluster
+	__attribute__((hot)) $thread * pop_fast(struct cluster * cltr) with (cltr->ready_queue) {
+		/* paranoid */ verify( lanes.count > 0 );
+		/* paranoid */ verify( kernelTLS().this_processor );
+
+		const int cpu = __kernel_getcpu();
+		/* paranoid */ verify(cpu >= 0);
+		/* paranoid */ verify(cpu < cpu_info.hthrd_count);
+		/* paranoid */ verify(cpu * READYQ_SHARD_FACTOR < lanes.count);
+
+		const cpu_map_entry_t & map = cpu_info.llc_map[cpu];
+		/* paranoid */ verify(map.start * READYQ_SHARD_FACTOR < lanes.count);
+		/* paranoid */ verify(map.self * READYQ_SHARD_FACTOR < lanes.count);
+		/* paranoid */ verifyf((map.start + map.count) * READYQ_SHARD_FACTOR <= lanes.count, "have %u lanes but map can go up to %u", lanes.count, (map.start + map.count) * READYQ_SHARD_FACTOR);
+
+		processor * const proc = kernelTLS().this_processor;
+		const int start = map.self * READYQ_SHARD_FACTOR;
+
+		// Did we already have a help target
+		if(proc->rdq.target == -1u) {
+			// if We don't have a
+			unsigned long long min = ts(lanes.data[start]);
+			for(i; READYQ_SHARD_FACTOR) {
+				unsigned long long tsc = ts(lanes.data[start + i]);
+				if(tsc < min) min = tsc;
+			}
+			proc->rdq.cutoff = min;
+			proc->rdq.target = (map.start * READYQ_SHARD_FACTOR) + (__tls_rand() % (map.count* READYQ_SHARD_FACTOR));
+		}
+		else {
+			const unsigned long long bias = 0; //2_500_000_000;
+			const unsigned long long cutoff = proc->rdq.cutoff > bias ? proc->rdq.cutoff - bias : proc->rdq.cutoff;
+			{
+				unsigned target = proc->rdq.target;
+				proc->rdq.target = -1u;
+				if(lanes.tscs[target].tv < cutoff && ts(lanes.data[target]) < cutoff) {
+					$thread * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
+					proc->rdq.last = target;
+					if(t) return t;
+				}
+			}
+
+			unsigned last = proc->rdq.last;
+			if(last != -1u && lanes.tscs[last].tv < cutoff && ts(lanes.data[last]) < cutoff) {
+				$thread * t = try_pop(cltr, last __STATS(, __tls_stats()->ready.pop.help));
+				if(t) return t;
+			}
+			else {
+				proc->rdq.last = -1u;
+			}
+		}
+
+		for(READYQ_SHARD_FACTOR) {
+			unsigned i = start + (proc->rdq.itr++ % READYQ_SHARD_FACTOR);
+			if($thread * t = try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.local))) return t;
+		}
+
+		// All lanes where empty return 0p
+		return 0p;
+	}
+
+	__attribute__((hot)) struct $thread * pop_slow(struct cluster * cltr) with (cltr->ready_queue) {
+		processor * const proc = kernelTLS().this_processor;
+		unsigned last = proc->rdq.last;
+
+		unsigned i = __tls_rand() % lanes.count;
+		return try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.steal));
+	}
+	__attribute__((hot)) struct $thread * pop_search(struct cluster * cltr) {
+		return search(cltr);
+	}
+#endif
 #if defined(USE_RELAXED_FIFO)
 	//-----------------------------------------------------------------------
@@ -580,128 +711,134 @@
 }
 
-// Grow the ready queue
-void ready_queue_grow(struct cluster * cltr) {
-	size_t ncount;
-	int target = cltr->procs.total;
-
-	/* paranoid */ verify( ready_mutate_islocked() );
-	__cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue\n");
-
-	// Make sure that everything is consistent
-	/* paranoid */ check( cltr->ready_queue );
-
-	// grow the ready queue
-	with( cltr->ready_queue ) {
-		// Find new count
-		// Make sure we always have atleast 1 list
-		if(target >= 2) {
-			ncount = target * READYQ_SHARD_FACTOR;
-		} else {
-			ncount = SEQUENTIAL_SHARD;
-		}
-
-		// Allocate new array (uses realloc and memcpies the data)
-		lanes.data = alloc( ncount, lanes.data`realloc );
-
-		// Fix the moved data
-		for( idx; (size_t)lanes.count ) {
-			fix(lanes.data[idx]);
-		}
-
-		// Construct new data
-		for( idx; (size_t)lanes.count ~ ncount) {
-			(lanes.data[idx]){};
-		}
-
-		// Update original
-		lanes.count = ncount;
-	}
-
-	fix_times(cltr);
-
-	reassign_cltr_id(cltr);
-
-	// Make sure that everything is consistent
-	/* paranoid */ check( cltr->ready_queue );
-
-	__cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue done\n");
-
-	/* paranoid */ verify( ready_mutate_islocked() );
-}
-
-// Shrink the ready queue
-void ready_queue_shrink(struct cluster * cltr) {
-	/* paranoid */ verify( ready_mutate_islocked() );
-	__cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue\n");
-
-	// Make sure that everything is consistent
-	/* paranoid */ check( cltr->ready_queue );
-
-	int target = cltr->procs.total;
-
-	with( cltr->ready_queue ) {
-		// Remember old count
-		size_t ocount = lanes.count;
-
-		// Find new count
-		// Make sure we always have atleast 1 list
-		lanes.count = target >= 2 ? target * READYQ_SHARD_FACTOR: SEQUENTIAL_SHARD;
-		/* paranoid */ verify( ocount >= lanes.count );
-		/* paranoid */ verify( lanes.count == target * READYQ_SHARD_FACTOR || target < 2 );
-
-		// for printing count the number of displaced threads
-		#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
-			__attribute__((unused)) size_t displaced = 0;
-		#endif
-
-		// redistribute old data
-		for( idx; (size_t)lanes.count ~ ocount) {
-			// Lock is not strictly needed but makes checking invariants much easier
-			__attribute__((unused)) bool locked = __atomic_try_acquire(&lanes.data[idx].lock);
-			verify(locked);
-
-			// As long as we can pop from this lane to push the threads somewhere else in the queue
-			while(!is_empty(lanes.data[idx])) {
-				struct $thread * thrd;
-				unsigned long long _;
-				[thrd, _] = pop(lanes.data[idx]);
-
-				push(cltr, thrd, true);
-
-				// for printing count the number of displaced threads
-				#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
-					displaced++;
-				#endif
-			}
-
-			// Unlock the lane
-			__atomic_unlock(&lanes.data[idx].lock);
-
-			// TODO print the queue statistics here
-
-			^(lanes.data[idx]){};
-		}
-
-		__cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue displaced %zu threads\n", displaced);
-
-		// Allocate new array (uses realloc and memcpies the data)
-		lanes.data = alloc( lanes.count, lanes.data`realloc );
-
-		// Fix the moved data
-		for( idx; (size_t)lanes.count ) {
-			fix(lanes.data[idx]);
-		}
-	}
-
-	fix_times(cltr);
-
-	reassign_cltr_id(cltr);
-
-	// Make sure that everything is consistent
-	/* paranoid */ check( cltr->ready_queue );
-
-	__cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue done\n");
-	/* paranoid */ verify( ready_mutate_islocked() );
-}
+#if defined(USE_CPU_WORK_STEALING)
+	// ready_queue size is fixed in this case
+	void ready_queue_grow(struct cluster * cltr) {}
+	void ready_queue_shrink(struct cluster * cltr) {}
+#else
+	// Grow the ready queue
+	void ready_queue_grow(struct cluster * cltr) {
+		size_t ncount;
+		int target = cltr->procs.total;
+
+		/* paranoid */ verify( ready_mutate_islocked() );
+		__cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue\n");
+
+		// Make sure that everything is consistent
+		/* paranoid */ check( cltr->ready_queue );
+
+		// grow the ready queue
+		with( cltr->ready_queue ) {
+			// Find new count
+			// Make sure we always have atleast 1 list
+			if(target >= 2) {
+				ncount = target * READYQ_SHARD_FACTOR;
+			} else {
+				ncount = SEQUENTIAL_SHARD;
+			}
+
+			// Allocate new array (uses realloc and memcpies the data)
+			lanes.data = alloc( ncount, lanes.data`realloc );
+
+			// Fix the moved data
+			for( idx; (size_t)lanes.count ) {
+				fix(lanes.data[idx]);
+			}
+
+			// Construct new data
+			for( idx; (size_t)lanes.count ~ ncount) {
+				(lanes.data[idx]){};
+			}
+
+			// Update original
+			lanes.count = ncount;
+		}
+
+		fix_times(cltr);
+
+		reassign_cltr_id(cltr);
+
+		// Make sure that everything is consistent
+		/* paranoid */ check( cltr->ready_queue );
+
+		__cfadbg_print_safe(ready_queue, "Kernel : Growing ready queue done\n");
+
+		/* paranoid */ verify( ready_mutate_islocked() );
+	}
+
+	// Shrink the ready queue
+	void ready_queue_shrink(struct cluster * cltr) {
+		/* paranoid */ verify( ready_mutate_islocked() );
+		__cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue\n");
+
+		// Make sure that everything is consistent
+		/* paranoid */ check( cltr->ready_queue );
+
+		int target = cltr->procs.total;
+
+		with( cltr->ready_queue ) {
+			// Remember old count
+			size_t ocount = lanes.count;
+
+			// Find new count
+			// Make sure we always have atleast 1 list
+			lanes.count = target >= 2 ? target * READYQ_SHARD_FACTOR: SEQUENTIAL_SHARD;
+			/* paranoid */ verify( ocount >= lanes.count );
+			/* paranoid */ verify( lanes.count == target * READYQ_SHARD_FACTOR || target < 2 );
+
+			// for printing count the number of displaced threads
+			#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
+				__attribute__((unused)) size_t displaced = 0;
+			#endif
+
+			// redistribute old data
+			for( idx; (size_t)lanes.count ~ ocount) {
+				// Lock is not strictly needed but makes checking invariants much easier
+				__attribute__((unused)) bool locked = __atomic_try_acquire(&lanes.data[idx].lock);
+				verify(locked);
+
+				// As long as we can pop from this lane to push the threads somewhere else in the queue
+				while(!is_empty(lanes.data[idx])) {
+					struct $thread * thrd;
+					unsigned long long _;
+					[thrd, _] = pop(lanes.data[idx]);
+
+					push(cltr, thrd, true);
+
+					// for printing count the number of displaced threads
+					#if defined(__CFA_DEBUG_PRINT__) || defined(__CFA_DEBUG_PRINT_READY_QUEUE__)
+						displaced++;
+					#endif
+				}
+
+				// Unlock the lane
+				__atomic_unlock(&lanes.data[idx].lock);
+
+				// TODO print the queue statistics here
+
+				^(lanes.data[idx]){};
+			}
+
+			__cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue displaced %zu threads\n", displaced);
+
+			// Allocate new array (uses realloc and memcpies the data)
+			lanes.data = alloc( lanes.count, lanes.data`realloc );
+
+			// Fix the moved data
+			for( idx; (size_t)lanes.count ) {
+				fix(lanes.data[idx]);
+			}
+		}
+
+		fix_times(cltr);
+
+		reassign_cltr_id(cltr);
+
+		// Make sure that everything is consistent
+		/* paranoid */ check( cltr->ready_queue );
+
+		__cfadbg_print_safe(ready_queue, "Kernel : Shrinking ready queue done\n");
+		/* paranoid */ verify( ready_mutate_islocked() );
+	}
+#endif
 
 #if !defined(__CFA_NO_STATISTICS__)
Index: libcfa/src/containers/array.hfa
===================================================================
--- libcfa/src/containers/array.hfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ libcfa/src/containers/array.hfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -1,13 +1,7 @@
 
 
-// a type whose size is n
-#define Z(n) char[n]
-
-// the inverse of Z(-)
-#define z(N) sizeof(N)
-
-forall( T & ) struct tag {};
+forall( __CFA_tysys_id_only_X & ) struct tag {};
 #define ttag(T) ((tag(T)){})
-#define ztag(n) ttag(Z(n))
+#define ztag(n) ttag(n)
 
 
@@ -18,5 +12,5 @@
 forall( [N], S & | sized(S), Timmed &, Tbase & ) {
     struct arpk {
-        S strides[z(N)];
+        S strides[N];
     };
 
@@ -56,14 +50,14 @@
 
     static inline size_t ?`len( arpk(N, S, Timmed, Tbase) & a ) {
-        return z(N);
+        return N;
     }
 
     // workaround #226 (and array relevance thereof demonstrated in mike102/otype-slow-ndims.cfa)
     static inline void ?{}( arpk(N, S, Timmed, Tbase) & this ) {
-        void ?{}( S (&inner)[z(N)] ) {}
+        void ?{}( S (&inner)[N] ) {}
         ?{}(this.strides);
     }
     static inline void ^?{}( arpk(N, S, Timmed, Tbase) & this ) {
-        void ^?{}( S (&inner)[z(N)] ) {}
+        void ^?{}( S (&inner)[N] ) {}
         ^?{}(this.strides);
     }
Index: libcfa/src/device/cpu.cfa
===================================================================
--- libcfa/src/device/cpu.cfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ libcfa/src/device/cpu.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -256,26 +256,36 @@
 }
 
+struct llc_map_t {
+	raw_cache_instance * raw;
+	unsigned count;
+	unsigned start;
+};
+
 // returns an allocate list of all the different distinct last level caches
-static [*idx_range_t, size_t cnt] distinct_llcs(unsigned cpus, unsigned llc_idx, raw_cache_instance ** raw) {
+static [*llc_map_t, size_t cnt] distinct_llcs(unsigned cpus, unsigned llc_idx, raw_cache_instance ** raw) {
 	// Allocate at least one element
-	idx_range_t * ranges = alloc();
+	llc_map_t* ranges = alloc();
 	size_t range_cnt = 1;
 
 	// Initialize with element 0
-	*ranges = raw[0][llc_idx].range;
+	ranges->raw = &raw[0][llc_idx];
+	ranges->count = 0;
+	ranges->start = -1u;
 
 	// Go over all other cpus
 	CPU_LOOP: for(i; 1~cpus) {
 		// Check if the range is already there
-		idx_range_t candidate = raw[i][llc_idx].range;
+		raw_cache_instance * candidate = &raw[i][llc_idx];
 		for(j; range_cnt) {
-			idx_range_t exist = ranges[j];
+			llc_map_t & exist = ranges[j];
 			// If the range is already there just jump to the next cpu
-			if(0 == strcmp(candidate, exist)) continue CPU_LOOP;
+			if(0 == strcmp(candidate->range, exist.raw->range)) continue CPU_LOOP;
 		}
 
 		// The range wasn't there, added to the list
 		ranges = alloc(range_cnt + 1, ranges`realloc);
-		ranges[range_cnt] = candidate;
+		ranges[range_cnt].raw = candidate;
+		ranges[range_cnt].count = 0;
+		ranges[range_cnt].start = -1u;
 		range_cnt++;
 	}
@@ -287,12 +297,12 @@
 struct cpu_pairing_t {
 	unsigned cpu;
-	unsigned llc_id;
+	unsigned id;
 };
 
 int ?<?( cpu_pairing_t lhs, cpu_pairing_t rhs ) {
-	return lhs.llc_id < rhs.llc_id;
-}
-
-static [[]cpu_pairing_t] get_cpu_pairings(unsigned cpus, raw_cache_instance ** raw, idx_range_t * maps, size_t map_cnt) {
+	return lhs.id < rhs.id;
+}
+
+static [[]cpu_pairing_t] get_cpu_pairings(unsigned cpus, raw_cache_instance ** raw, llc_map_t * maps, size_t map_cnt) {
 	cpu_pairing_t * pairings = alloc(cpus);
 
@@ -301,7 +311,7 @@
 		idx_range_t want = raw[i][0].range;
 		MAP_LOOP: for(j; map_cnt) {
-			if(0 != strcmp(want, maps[j])) continue MAP_LOOP;
-
-			pairings[i].llc_id = j;
+			if(0 != strcmp(want, maps[j].raw->range)) continue MAP_LOOP;
+
+			pairings[i].id = j;
 			continue CPU_LOOP;
 		}
@@ -312,4 +322,6 @@
 	return pairings;
 }
+
+#include <fstream.hfa>
 
 extern "C" {
@@ -336,16 +348,20 @@
 
 		// Find number of distinct cache instances
-		idx_range_t * maps;
+		llc_map_t * maps;
 		size_t map_cnt;
 		[maps, map_cnt] =  distinct_llcs(cpus, cache_levels - llc, raw);
 
 		#if defined(__CFA_WITH_VERIFY__)
+		// Verify that the caches cover the all the cpus
 		{
-			unsigned width = 0;
+			unsigned width1 = 0;
+			unsigned width2 = 0;
 			for(i; map_cnt) {
 				const char * _;
-				width += read_width(maps[i], strlen(maps[i]), &_);
+				width1 += read_width(maps[i].raw->range, strlen(maps[i].raw->range), &_);
+				width2 += maps[i].raw->width;
 			}
-			verify(width == cpus);
+			verify(width1 == cpus);
+			verify(width2 == cpus);
 		}
 		#endif
@@ -357,13 +373,31 @@
 		qsort(pairings, cpus);
 
-		unsigned llc_width = raw[0][cache_levels - llc].width;
-
-		// From the mappins build the actual cpu map we want
+		{
+			unsigned it = 0;
+			for(i; cpus) {
+				unsigned llc_id = pairings[i].id;
+				if(maps[llc_id].start == -1u) {
+					maps[llc_id].start = it;
+					it += maps[llc_id].raw->width;
+					/* paranoid */ verify(maps[llc_id].start < it);
+					/* paranoid */ verify(it != -1u);
+				}
+			}
+			/* paranoid */ verify(it == cpus);
+		}
+
+		// From the mappings build the actual cpu map we want
 		struct cpu_map_entry_t * entries = alloc(cpus);
 		for(i; cpus) { entries[i].count = 0; }
 		for(i; cpus) {
+			/* paranoid */ verify(pairings[i].id < map_cnt);
 			unsigned c = pairings[i].cpu;
-			entries[c].start = pairings[i].llc_id * llc_width;
-			entries[c].count = llc_width;
+			unsigned llc_id = pairings[i].id;
+			unsigned width = maps[llc_id].raw->width;
+			unsigned start = maps[llc_id].start;
+			unsigned self  = start + (maps[llc_id].count++);
+			entries[c].count = width;
+			entries[c].start = start;
+			entries[c].self  = self;
 		}
 
Index: libcfa/src/device/cpu.hfa
===================================================================
--- libcfa/src/device/cpu.hfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ libcfa/src/device/cpu.hfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -17,4 +17,5 @@
 
 struct cpu_map_entry_t {
+	unsigned self;
 	unsigned start;
 	unsigned count;
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/AST/Convert.cpp	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -2415,4 +2415,15 @@
 	}
 
+	virtual void visit( const DimensionExpr * old ) override final {
+		// DimensionExpr gets desugared away in Validate.
+		// As long as new-AST passes don't use it, this cheap-cheerful error
+		// detection helps ensure that these occurrences have been compiled
+		// away, as expected.  To move the DimensionExpr boundary downstream
+		// or move the new-AST translation boundary upstream, implement
+		// DimensionExpr in the new AST and implement a conversion.
+		(void) old;
+		assert(false && "DimensionExpr should not be present at new-AST boundary");
+	}
+
 	virtual void visit( const AsmExpr * old ) override final {
 		this->node = visitBaseExpr( old,
Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/AST/Decl.cpp	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -78,5 +78,5 @@
 
 const char * TypeDecl::typeString() const {
-	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized array length type" };
+	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized length value" };
 	static_assert( sizeof(kindNames) / sizeof(kindNames[0]) == TypeDecl::NUMBER_OF_KINDS, "typeString: kindNames is out of sync." );
 	assertf( kind < TypeDecl::NUMBER_OF_KINDS, "TypeDecl kind is out of bounds." );
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/AST/Decl.hpp	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -175,5 +175,5 @@
 class TypeDecl final : public NamedTypeDecl {
   public:
-	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, ALtype, NUMBER_OF_KINDS };
+	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, Dimension, NUMBER_OF_KINDS };
 
 	Kind kind;
Index: src/CodeGen/CodeGenerator.cc
===================================================================
--- src/CodeGen/CodeGenerator.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/CodeGen/CodeGenerator.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -589,4 +589,9 @@
 			output << nameExpr->get_name();
 		} // if
+	}
+
+	void CodeGenerator::postvisit( DimensionExpr * dimensionExpr ) {
+		extension( dimensionExpr );
+		output << "/*non-type*/" << dimensionExpr->get_name();
 	}
 
Index: src/CodeGen/CodeGenerator.h
===================================================================
--- src/CodeGen/CodeGenerator.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/CodeGen/CodeGenerator.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -92,4 +92,5 @@
 		void postvisit( TupleIndexExpr * tupleExpr );
 		void postvisit( TypeExpr *typeExpr );
+		void postvisit( DimensionExpr *dimensionExpr );
 		void postvisit( AsmExpr * );
 		void postvisit( StmtExpr * );
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/Common/PassVisitor.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -167,4 +167,6 @@
 	virtual void visit( TypeExpr * typeExpr ) override final;
 	virtual void visit( const TypeExpr * typeExpr ) override final;
+	virtual void visit( DimensionExpr * dimensionExpr ) override final;
+	virtual void visit( const DimensionExpr * dimensionExpr ) override final;
 	virtual void visit( AsmExpr * asmExpr ) override final;
 	virtual void visit( const AsmExpr * asmExpr ) override final;
@@ -309,4 +311,5 @@
 	virtual Expression * mutate( CommaExpr * commaExpr ) override final;
 	virtual Expression * mutate( TypeExpr * typeExpr ) override final;
+	virtual Expression * mutate( DimensionExpr * dimensionExpr ) override final;
 	virtual Expression * mutate( AsmExpr * asmExpr ) override final;
 	virtual Expression * mutate( ImplicitCopyCtorExpr * impCpCtorExpr ) override final;
@@ -542,5 +545,5 @@
 class WithIndexer {
 protected:
-	WithIndexer() {}
+	WithIndexer( bool trackIdentifiers = true ) : indexer(trackIdentifiers) {}
 	~WithIndexer() {}
 
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/Common/PassVisitor.impl.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -2519,4 +2519,34 @@
 
 //--------------------------------------------------------------------------
+// DimensionExpr
+template< typename pass_type >
+void PassVisitor< pass_type >::visit( DimensionExpr * node ) {
+	VISIT_START( node );
+
+	indexerScopedAccept( node->result, *this );
+
+	VISIT_END( node );
+}
+
+template< typename pass_type >
+void PassVisitor< pass_type >::visit( const DimensionExpr * node ) {
+	VISIT_START( node );
+
+	indexerScopedAccept( node->result, *this );
+
+	VISIT_END( node );
+}
+
+template< typename pass_type >
+Expression * PassVisitor< pass_type >::mutate( DimensionExpr * node ) {
+	MUTATE_START( node );
+
+	indexerScopedMutate( node->env   , *this );
+	indexerScopedMutate( node->result, *this );
+
+	MUTATE_END( Expression, node );
+}
+
+//--------------------------------------------------------------------------
 // AsmExpr
 template< typename pass_type >
@@ -3157,5 +3187,5 @@
 
 	maybeAccept_impl( node->forall, *this );
-	// xxx - should PointerType visit/mutate dimension?
+	maybeAccept_impl( node->dimension, *this );
 	maybeAccept_impl( node->base, *this );
 
@@ -3168,5 +3198,5 @@
 
 	maybeAccept_impl( node->forall, *this );
-	// xxx - should PointerType visit/mutate dimension?
+	maybeAccept_impl( node->dimension, *this );
 	maybeAccept_impl( node->base, *this );
 
@@ -3179,5 +3209,5 @@
 
 	maybeMutate_impl( node->forall, *this );
-	// xxx - should PointerType visit/mutate dimension?
+	maybeMutate_impl( node->dimension, *this );
 	maybeMutate_impl( node->base, *this );
 
Index: src/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/Parser/DeclarationNode.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -1076,8 +1076,8 @@
 	if ( variable.tyClass != TypeDecl::NUMBER_OF_KINDS ) {
 		// otype is internally converted to dtype + otype parameters
-		static const TypeDecl::Kind kindMap[] = { TypeDecl::Dtype, TypeDecl::DStype, TypeDecl::Dtype, TypeDecl::Ftype, TypeDecl::Ttype, TypeDecl::Dtype };
+		static const TypeDecl::Kind kindMap[] = { TypeDecl::Dtype, TypeDecl::DStype, TypeDecl::Dtype, TypeDecl::Ftype, TypeDecl::Ttype, TypeDecl::Dimension };
 		static_assert( sizeof(kindMap) / sizeof(kindMap[0]) == TypeDecl::NUMBER_OF_KINDS, "DeclarationNode::build: kindMap is out of sync." );
 		assertf( variable.tyClass < sizeof(kindMap)/sizeof(kindMap[0]), "Variable's tyClass is out of bounds." );
-		TypeDecl * ret = new TypeDecl( *name, Type::StorageClasses(), nullptr, kindMap[ variable.tyClass ], variable.tyClass == TypeDecl::Otype || variable.tyClass == TypeDecl::ALtype, variable.initializer ? variable.initializer->buildType() : nullptr );
+		TypeDecl * ret = new TypeDecl( *name, Type::StorageClasses(), nullptr, kindMap[ variable.tyClass ], variable.tyClass == TypeDecl::Otype, variable.initializer ? variable.initializer->buildType() : nullptr );
 		buildList( variable.assertions, ret->get_assertions() );
 		return ret;
Index: src/Parser/ExpressionNode.cc
===================================================================
--- src/Parser/ExpressionNode.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/Parser/ExpressionNode.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -509,4 +509,9 @@
 } // build_varref
 
+DimensionExpr * build_dimensionref( const string * name ) {
+	DimensionExpr * expr = new DimensionExpr( *name );
+	delete name;
+	return expr;
+} // build_varref
 // TODO: get rid of this and OperKinds and reuse code from OperatorTable
 static const char * OperName[] = {						// must harmonize with OperKinds
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/Parser/ParseNode.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -183,4 +183,5 @@
 
 NameExpr * build_varref( const std::string * name );
+DimensionExpr * build_dimensionref( const std::string * name );
 
 Expression * build_cast( DeclarationNode * decl_node, ExpressionNode * expr_node );
Index: src/Parser/TypedefTable.cc
===================================================================
--- src/Parser/TypedefTable.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/Parser/TypedefTable.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -10,6 +10,6 @@
 // Created On       : Sat May 16 15:20:13 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Mar 15 20:56:47 2021
-// Update Count     : 260
+// Last Modified On : Wed May 19 08:30:14 2021
+// Update Count     : 262
 //
 
@@ -31,4 +31,5 @@
 	switch ( kind ) {
 	  case IDENTIFIER: return "identifier";
+	  case TYPEDIMname: return "typedim";
 	  case TYPEDEFname: return "typedef";
 	  case TYPEGENname: return "typegen";
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/Parser/parser.yy	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -10,6 +10,6 @@
 // Created On       : Sat Sep  1 20:22:55 2001
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Apr 26 18:41:54 2021
-// Update Count     : 4990
+// Last Modified On : Wed May 19 14:20:36 2021
+// Update Count     : 5022
 //
 
@@ -287,5 +287,5 @@
 
 // names and constants: lexer differentiates between identifier and typedef names
-%token<tok> IDENTIFIER		QUOTED_IDENTIFIER	TYPEDEFname		TYPEGENname
+%token<tok> IDENTIFIER		QUOTED_IDENTIFIER	TYPEDIMname		TYPEDEFname		TYPEGENname
 %token<tok> TIMEOUT			WOR					CATCH			RECOVER			CATCHRESUME		FIXUP		FINALLY		// CFA
 %token<tok> INTEGERconstant	CHARACTERconstant	STRINGliteral
@@ -586,4 +586,8 @@
 	| quasi_keyword
 		{ $$ = new ExpressionNode( build_varref( $1 ) ); }
+	| TYPEDIMname										// CFA, generic length argument
+		// { $$ = new ExpressionNode( new TypeExpr( maybeMoveBuildType( DeclarationNode::newFromTypedef( $1 ) ) ) ); }
+		// { $$ = new ExpressionNode( build_varref( $1 ) ); }
+		{ $$ = new ExpressionNode( build_dimensionref( $1 ) ); }
 	| tuple
 	| '(' comma_expression ')'
@@ -2535,6 +2539,6 @@
 	| '[' identifier_or_type_name ']'
 		{
-			typedefTable.addToScope( *$2, TYPEDEFname, "9" );
-			$$ = DeclarationNode::newTypeParam( TypeDecl::ALtype, $2 );
+			typedefTable.addToScope( *$2, TYPEDIMname, "9" );
+			$$ = DeclarationNode::newTypeParam( TypeDecl::Dimension, $2 );
 		}
 	// | type_specifier identifier_parameter_declarator
@@ -2590,10 +2594,8 @@
 		{ $$ = new ExpressionNode( new TypeExpr( maybeMoveBuildType( $1 ) ) ); }
 	| assignment_expression
-		{ SemanticError( yylloc, toString("Expression generic parameters are currently unimplemented: ", $1->build()) ); $$ = nullptr; }
 	| type_list ',' type
 		{ $$ = (ExpressionNode *)($1->set_last( new ExpressionNode( new TypeExpr( maybeMoveBuildType( $3 ) ) ) )); }
 	| type_list ',' assignment_expression
-		{ SemanticError( yylloc, toString("Expression generic parameters are currently unimplemented: ", $3->build()) ); $$ = nullptr; }
-		// { $$ = (ExpressionNode *)( $1->set_last( $3 )); }
+		{ $$ = (ExpressionNode *)( $1->set_last( $3 )); }
 	;
 
Index: src/SymTab/Indexer.cc
===================================================================
--- src/SymTab/Indexer.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SymTab/Indexer.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -74,7 +74,7 @@
 	}
 
-	Indexer::Indexer()
+	Indexer::Indexer( bool trackIdentifiers )
 	: idTable(), typeTable(), structTable(), enumTable(), unionTable(), traitTable(),
-	  prevScope(), scope( 0 ), repScope( 0 ) { ++* stats().count; }
+	  prevScope(), scope( 0 ), repScope( 0 ), trackIdentifiers( trackIdentifiers ) { ++* stats().count; }
 
 	Indexer::~Indexer() {
@@ -110,4 +110,6 @@
 
 	void Indexer::lookupId( const std::string & id, std::list< IdData > &out ) const {
+		assert( trackIdentifiers );
+
 		++* stats().lookup_calls;
 		if ( ! idTable ) return;
@@ -434,4 +436,5 @@
 			const Declaration * deleteStmt ) {
 		++* stats().add_calls;
+		if ( ! trackIdentifiers ) return;
 		const std::string &name = decl->name;
 		if ( name == "" ) return;
Index: src/SymTab/Indexer.h
===================================================================
--- src/SymTab/Indexer.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SymTab/Indexer.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -31,5 +31,5 @@
 	class Indexer : public std::enable_shared_from_this<SymTab::Indexer> {
 	public:
-		explicit Indexer();
+		explicit Indexer( bool trackIdentifiers = true );
 		virtual ~Indexer();
 
@@ -180,4 +180,6 @@
 		/// returns true if there exists a declaration with C linkage and the given name with a different mangled name
 		bool hasIncompatibleCDecl( const std::string & id, const std::string & mangleName ) const;
+
+	    bool trackIdentifiers;
 	};
 } // namespace SymTab
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SymTab/Validate.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -105,4 +105,5 @@
 
 	struct FixQualifiedTypes final : public WithIndexer {
+		FixQualifiedTypes() : WithIndexer(false) {}
 		Type * postmutate( QualifiedType * );
 	};
@@ -174,4 +175,14 @@
 	};
 
+	/// Does early resolution on the expressions that give enumeration constants their values
+	struct ResolveEnumInitializers final : public WithIndexer, public WithGuards, public WithVisitorRef<ResolveEnumInitializers>, public WithShortCircuiting {
+		ResolveEnumInitializers( const Indexer * indexer );
+		void postvisit( EnumDecl * enumDecl );
+
+	  private:
+		const Indexer * local_indexer;
+
+	};
+
 	/// Replaces array and function types in forall lists by appropriate pointer type and assigns each Object and Function declaration a unique ID.
 	struct ForallPointerDecay_old final {
@@ -260,4 +271,23 @@
 		void previsit( StructInstType * inst );
 		void previsit( UnionInstType * inst );
+	};
+
+	/// desugar declarations and uses of dimension paramaters like [N],
+	/// from type-system managed values, to tunnneling via ordinary types,
+	/// as char[-] in and sizeof(-) out
+	struct TranslateDimensionGenericParameters : public WithIndexer, public WithGuards {
+		static void translateDimensions( std::list< Declaration * > &translationUnit );
+		TranslateDimensionGenericParameters();
+
+		bool nextVisitedNodeIsChildOfSUIT = false; // SUIT = Struct or Union -Inst Type
+		bool visitingChildOfSUIT = false;
+		void changeState_ChildOfSUIT( bool newVal );
+		void premutate( StructInstType * sit );
+		void premutate( UnionInstType * uit );
+		void premutate( BaseSyntaxNode * node );
+
+		TypeDecl * postmutate( TypeDecl * td );
+		Expression * postmutate( DimensionExpr * de );
+		Expression * postmutate( Expression * e );
 	};
 
@@ -307,4 +337,5 @@
 		PassVisitor<EnumAndPointerDecay_old> epc;
 		PassVisitor<LinkReferenceToTypes_old> lrt( nullptr );
+		PassVisitor<ResolveEnumInitializers> rei( nullptr );
 		PassVisitor<ForallPointerDecay_old> fpd;
 		PassVisitor<CompoundLiteral> compoundliteral;
@@ -326,23 +357,27 @@
 			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_old, 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 ); //
-			});
+			acceptAll( translationUnit, lrt ); // must happen before autogen, because sized flag needs to propagate to generated functions
+			mutateAll( translationUnit, fixQual ); // must happen after LinkReferenceToTypes_old, because aggregate members are accessed
+			HoistStruct::hoistStruct( translationUnit );
+			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_old
-			ReturnChecker::checkFunctionReturns( translationUnit );
-			InitTweak::fixReturnStatements( translationUnit ); // must happen before autogen
+			Stats::Time::TimeBlock("Validate Generic Parameters", [&]() {
+				acceptAll( translationUnit, genericParams );  // check as early as possible - can't happen before LinkReferenceToTypes_old; observed failing when attempted before eliminateTypedef
+			});
+			Stats::Time::TimeBlock("Translate Dimensions", [&]() {
+				TranslateDimensionGenericParameters::translateDimensions( translationUnit );
+			});
+			Stats::Time::TimeBlock("Resolve Enum Initializers", [&]() {
+				acceptAll( translationUnit, rei ); // must happen after translateDimensions because rei needs identifier lookup, which needs name mangling
+			});
+			Stats::Time::TimeBlock("Check Function Returns", [&]() {
+				ReturnChecker::checkFunctionReturns( translationUnit );
+			});
+			Stats::Time::TimeBlock("Fix Return Statements", [&]() {
+				InitTweak::fixReturnStatements( translationUnit ); // must happen before autogen
+			});
 		}
 		{
@@ -644,5 +679,5 @@
 	}
 
-	LinkReferenceToTypes_old::LinkReferenceToTypes_old( const Indexer * other_indexer ) {
+	LinkReferenceToTypes_old::LinkReferenceToTypes_old( const Indexer * other_indexer ) : WithIndexer( false ) {
 		if ( other_indexer ) {
 			local_indexer = other_indexer;
@@ -664,12 +699,4 @@
 	}
 
-	void checkGenericParameters( ReferenceToType * inst ) {
-		for ( Expression * param : inst->parameters ) {
-			if ( ! dynamic_cast< TypeExpr * >( param ) ) {
-				SemanticError( inst, "Expression parameters for generic types are currently unsupported: " );
-			}
-		}
-	}
-
 	void LinkReferenceToTypes_old::postvisit( StructInstType * structInst ) {
 		const StructDecl * st = local_indexer->lookupStruct( structInst->name );
@@ -682,5 +709,4 @@
 			forwardStructs[ structInst->name ].push_back( structInst );
 		} // if
-		checkGenericParameters( structInst );
 	}
 
@@ -695,5 +721,4 @@
 			forwardUnions[ unionInst->name ].push_back( unionInst );
 		} // if
-		checkGenericParameters( unionInst );
 	}
 
@@ -807,13 +832,4 @@
 				forwardEnums.erase( fwds );
 			} // if
-
-			for ( Declaration * member : enumDecl->members ) {
-				ObjectDecl * field = strict_dynamic_cast<ObjectDecl *>( member );
-				if ( field->init ) {
-					// need to resolve enumerator initializers early so that other passes that determine if an expression is constexpr have the appropriate information.
-					SingleInit * init = strict_dynamic_cast<SingleInit *>( field->init );
-					ResolvExpr::findSingleExpression( init->value, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), indexer );
-				}
-			}
 		} // if
 	}
@@ -878,4 +894,25 @@
 				typeInst->set_isFtype( typeDecl->kind == TypeDecl::Ftype );
 			} // if
+		} // if
+	}
+
+	ResolveEnumInitializers::ResolveEnumInitializers( const Indexer * other_indexer ) : WithIndexer( true ) {
+		if ( other_indexer ) {
+			local_indexer = other_indexer;
+		} else {
+			local_indexer = &indexer;
+		} // if
+	}
+
+	void ResolveEnumInitializers::postvisit( EnumDecl * enumDecl ) {
+		if ( enumDecl->body ) {
+			for ( Declaration * member : enumDecl->members ) {
+				ObjectDecl * field = strict_dynamic_cast<ObjectDecl *>( member );
+				if ( field->init ) {
+					// need to resolve enumerator initializers early so that other passes that determine if an expression is constexpr have the appropriate information.
+					SingleInit * init = strict_dynamic_cast<SingleInit *>( field->init );
+					ResolvExpr::findSingleExpression( init->value, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), indexer );
+				}
+			}
 		} // if
 	}
@@ -1223,4 +1260,22 @@
 	}
 
+	// Test for special name on a generic parameter.  Special treatment for the
+	// special name is a bootstrapping hack.  In most cases, the worlds of T's
+	// and of N's don't overlap (normal treamtemt).  The foundations in
+	// array.hfa use tagging for both types and dimensions.  Tagging treats
+	// its subject parameter even more opaquely than T&, which assumes it is
+	// possible to have a pointer/reference to such an object.  Tagging only
+	// seeks to identify the type-system resident at compile time.  Both N's
+	// and T's can make tags.  The tag definition uses the special name, which
+	// is treated as "an N or a T."  This feature is not inteded to be used
+	// outside of the definition and immediate uses of a tag.
+	static inline bool isReservedTysysIdOnlyName( const std::string & name ) {
+		// name's prefix was __CFA_tysys_id_only, before it got wrapped in __..._generic
+		int foundAt = name.find("__CFA_tysys_id_only");
+		if (foundAt == 0) return true;
+		if (foundAt == 2 && name[0] == '_' && name[1] == '_') return true;
+		return false;
+	}
+
 	template< typename Aggr >
 	void validateGeneric( Aggr * inst ) {
@@ -1239,19 +1294,35 @@
 			TypeSubstitution sub;
 			auto paramIter = params->begin();
-			for ( size_t i = 0; paramIter != params->end(); ++paramIter, ++i ) {
-				if ( i < args.size() ) {
-					TypeExpr * expr = strict_dynamic_cast< TypeExpr * >( * std::next( args.begin(), i ) );
-					sub.add( (* paramIter)->get_name(), expr->get_type()->clone() );
-				} else if ( i == args.size() ) {
+			auto argIter = args.begin();
+			for ( ; paramIter != params->end(); ++paramIter, ++argIter ) {
+				if ( argIter != args.end() ) {
+					TypeExpr * expr = dynamic_cast< TypeExpr * >( * argIter );
+					if ( expr ) {
+						sub.add( (* paramIter)->get_name(), expr->get_type()->clone() );
+					}
+				} else {
 					Type * defaultType = (* paramIter)->get_init();
 					if ( defaultType ) {
 						args.push_back( new TypeExpr( defaultType->clone() ) );
 						sub.add( (* paramIter)->get_name(), defaultType->clone() );
+						argIter = std::prev(args.end());
+					} else {
+						SemanticError( inst, "Too few type arguments in generic type " );
 					}
 				}
+				assert( argIter != args.end() );
+				bool typeParamDeclared = (*paramIter)->kind != TypeDecl::Kind::Dimension;
+				bool typeArgGiven;
+				if ( isReservedTysysIdOnlyName( (*paramIter)->name ) ) {
+					// coerce a match when declaration is reserved name, which means "either"
+					typeArgGiven = typeParamDeclared;
+				} else {
+					typeArgGiven = dynamic_cast< TypeExpr * >( * argIter );
+				}
+				if ( ! typeParamDeclared &&   typeArgGiven ) SemanticError( inst, "Type argument given for value parameter: " );
+				if (   typeParamDeclared && ! typeArgGiven ) SemanticError( inst, "Expression argument given for type parameter: " );
 			}
 
 			sub.apply( inst );
-			if ( args.size() < params->size() ) SemanticError( inst, "Too few type arguments in generic type " );
 			if ( args.size() > params->size() ) SemanticError( inst, "Too many type arguments in generic type " );
 		}
@@ -1264,4 +1335,104 @@
 	void ValidateGenericParameters::previsit( UnionInstType * inst ) {
 		validateGeneric( inst );
+	}
+
+	void TranslateDimensionGenericParameters::translateDimensions( std::list< Declaration * > &translationUnit ) {
+		PassVisitor<TranslateDimensionGenericParameters> translator;
+		mutateAll( translationUnit, translator );
+	}
+
+	TranslateDimensionGenericParameters::TranslateDimensionGenericParameters() : WithIndexer( false ) {}
+
+	// Declaration of type variable:           forall( [N] )          ->  forall( N & | sized( N ) )
+	TypeDecl * TranslateDimensionGenericParameters::postmutate( TypeDecl * td ) {
+		if ( td->kind == TypeDecl::Dimension ) {
+			td->kind = TypeDecl::Dtype;
+			if ( ! isReservedTysysIdOnlyName( td->name ) ) {
+				td->sized = true;
+			}
+		}
+		return td;
+	}
+
+	// Situational awareness:
+	// array( float, [[currentExpr]]     )  has  visitingChildOfSUIT == true
+	// array( float, [[currentExpr]] - 1 )  has  visitingChildOfSUIT == false
+	// size_t x =    [[currentExpr]]        has  visitingChildOfSUIT == false
+	void TranslateDimensionGenericParameters::changeState_ChildOfSUIT( bool newVal ) {
+		GuardValue( nextVisitedNodeIsChildOfSUIT );
+		GuardValue( visitingChildOfSUIT );
+		visitingChildOfSUIT = nextVisitedNodeIsChildOfSUIT;
+		nextVisitedNodeIsChildOfSUIT = newVal;
+	}
+	void TranslateDimensionGenericParameters::premutate( StructInstType * sit ) {
+		(void) sit;
+		changeState_ChildOfSUIT(true);
+	}
+	void TranslateDimensionGenericParameters::premutate( UnionInstType * uit ) {
+		(void) uit;
+		changeState_ChildOfSUIT(true);
+	}
+	void TranslateDimensionGenericParameters::premutate( BaseSyntaxNode * node ) {
+		(void) node;
+		changeState_ChildOfSUIT(false);
+	}
+
+	// Passing values as dimension arguments:  array( float,     7 )  -> array( float, char[             7 ] )
+	// Consuming dimension parameters:         size_t x =    N - 1 ;  -> size_t x =          sizeof(N) - 1   ;
+	// Intertwined reality:                    array( float, N     )  -> array( float,              N        )
+	//                                         array( float, N - 1 )  -> array( float, char[ sizeof(N) - 1 ] )
+	// Intertwined case 1 is not just an optimization.
+	// Avoiding char[sizeof(-)] is necessary to enable the call of f to bind the value of N, in:
+	//   forall([N]) void f( array(float, N) & );
+	//   array(float, 7) a;
+	//   f(a);
+
+	Expression * TranslateDimensionGenericParameters::postmutate( DimensionExpr * de ) {
+		// Expression de is an occurrence of N in LHS of above examples.
+		// Look up the name that de references.
+		// If we are in a struct body, then this reference can be to an entry of the stuct's forall list.
+		// Whether or not we are in a struct body, this reference can be to an entry of a containing function's forall list.
+		// If we are in a struct body, then the stuct's forall declarations are innermost (functions don't occur in structs).
+		// Thus, a potential struct's declaration is highest priority.
+		// A struct's forall declarations are already renamed with _generic_ suffix.  Try that name variant first.
+
+		std::string useName = "__" + de->name + "_generic_";
+		TypeDecl * namedParamDecl = const_cast<TypeDecl *>( strict_dynamic_cast<const TypeDecl *, nullptr >( indexer.lookupType( useName ) ) );
+
+		if ( ! namedParamDecl ) {
+			useName = de->name;
+			namedParamDecl = const_cast<TypeDecl *>( strict_dynamic_cast<const TypeDecl *, nullptr >( indexer.lookupType( useName ) ) );
+		}
+
+		// Expect to find it always.  A misspelled name would have been parsed as an identifier.
+		assert( namedParamDecl && "Type-system-managed value name not found in symbol table" );
+
+		delete de;
+
+		TypeInstType * refToDecl = new TypeInstType( 0, useName, namedParamDecl );
+
+		if ( visitingChildOfSUIT ) {
+			// As in postmutate( Expression * ), topmost expression needs a TypeExpr wrapper
+			// But avoid ArrayType-Sizeof
+			return new TypeExpr( refToDecl );
+		} else {
+			// the N occurrence is being used directly as a runtime value,
+			// if we are in a type instantiation, then the N is within a bigger value computation
+			return new SizeofExpr( refToDecl );
+		}
+	}
+
+	Expression * TranslateDimensionGenericParameters::postmutate( Expression * e ) {
+		if ( visitingChildOfSUIT ) {
+			// e is an expression used as an argument to instantiate a type
+			if (! dynamic_cast< TypeExpr * >( e ) ) {
+				// e is a value expression
+				// but not a DimensionExpr, which has a distinct postmutate
+				Type * typeExprContent = new ArrayType( 0, new BasicType( 0, BasicType::Char ), e, true, false );
+				TypeExpr * result = new TypeExpr( typeExprContent );
+				return result;
+			}
+		}
+		return e;
 	}
 
Index: src/SynTree/Declaration.h
===================================================================
--- src/SynTree/Declaration.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SynTree/Declaration.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -201,5 +201,5 @@
 	typedef NamedTypeDecl Parent;
   public:
-	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, ALtype, NUMBER_OF_KINDS };
+	enum Kind { Dtype, DStype, Otype, Ftype, Ttype, Dimension, NUMBER_OF_KINDS };
 
 	Kind kind;
Index: src/SynTree/Expression.h
===================================================================
--- src/SynTree/Expression.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SynTree/Expression.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -587,4 +587,23 @@
 };
 
+/// DimensionExpr represents a type-system provided value used in an expression ( forrall([N]) ... N + 1 )
+class DimensionExpr : public Expression {
+  public:
+	std::string name;
+
+	DimensionExpr( std::string name );
+	DimensionExpr( const DimensionExpr & other );
+	virtual ~DimensionExpr();
+
+	const std::string & get_name() const { return name; }
+	void set_name( std::string newValue ) { name = newValue; }
+
+	virtual DimensionExpr * clone() const override { return new DimensionExpr( * this ); }
+	virtual void accept( Visitor & v ) override { v.visit( this ); }
+	virtual void accept( Visitor & v ) const override { v.visit( this ); }
+	virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); }
+	virtual void print( std::ostream & os, Indenter indent = {} ) const override;
+};
+
 /// AsmExpr represents a GCC 'asm constraint operand' used in an asm statement: [output] "=f" (result)
 class AsmExpr : public Expression {
Index: src/SynTree/Mutator.h
===================================================================
--- src/SynTree/Mutator.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SynTree/Mutator.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -80,4 +80,5 @@
 	virtual Expression * mutate( CommaExpr * commaExpr ) = 0;
 	virtual Expression * mutate( TypeExpr * typeExpr ) = 0;
+	virtual Expression * mutate( DimensionExpr * dimensionExpr ) = 0;
 	virtual Expression * mutate( AsmExpr * asmExpr ) = 0;
 	virtual Expression * mutate( ImplicitCopyCtorExpr * impCpCtorExpr ) = 0;
Index: src/SynTree/SynTree.h
===================================================================
--- src/SynTree/SynTree.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SynTree/SynTree.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -85,4 +85,5 @@
 class CommaExpr;
 class TypeExpr;
+class DimensionExpr;
 class AsmExpr;
 class ImplicitCopyCtorExpr;
Index: src/SynTree/TypeDecl.cc
===================================================================
--- src/SynTree/TypeDecl.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SynTree/TypeDecl.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -33,5 +33,5 @@
 
 const char * TypeDecl::typeString() const {
-	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized array length type" };
+	static const char * kindNames[] = { "sized data type", "sized data type", "sized object type", "sized function type", "sized tuple type", "sized length value" };
 	static_assert( sizeof(kindNames) / sizeof(kindNames[0]) == TypeDecl::NUMBER_OF_KINDS, "typeString: kindNames is out of sync." );
 	assertf( kind < TypeDecl::NUMBER_OF_KINDS, "TypeDecl kind is out of bounds." );
Index: src/SynTree/TypeExpr.cc
===================================================================
--- src/SynTree/TypeExpr.cc	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SynTree/TypeExpr.cc	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -35,4 +35,18 @@
 }
 
+DimensionExpr::DimensionExpr( std::string name ) : Expression(), name(name) {
+	assertf(name != "0", "Zero is not a valid name");
+	assertf(name != "1", "One is not a valid name");
+}
+
+DimensionExpr::DimensionExpr( const DimensionExpr & other ) : Expression( other ), name( other.name ) {
+}
+
+DimensionExpr::~DimensionExpr() {}
+
+void DimensionExpr::print( std::ostream & os, Indenter indent ) const {
+	os << "Type-Sys Value: " << get_name();
+	Expression::print( os, indent );
+}
 // Local Variables: //
 // tab-width: 4 //
Index: src/SynTree/Visitor.h
===================================================================
--- src/SynTree/Visitor.h	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ src/SynTree/Visitor.h	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -135,4 +135,6 @@
 	virtual void visit( TypeExpr * node ) { visit( const_cast<const TypeExpr *>(node) ); }
 	virtual void visit( const TypeExpr * typeExpr ) = 0;
+	virtual void visit( DimensionExpr * node ) { visit( const_cast<const DimensionExpr *>(node) ); }
+	virtual void visit( const DimensionExpr * typeExpr ) = 0;
 	virtual void visit( AsmExpr * node ) { visit( const_cast<const AsmExpr *>(node) ); }
 	virtual void visit( const AsmExpr * asmExpr ) = 0;
Index: tests/array-container/.expect/array-md-sbscr-cases.x86.txt
===================================================================
--- tests/array-container/.expect/array-md-sbscr-cases.x86.txt	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
+++ tests/array-container/.expect/array-md-sbscr-cases.x86.txt	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -0,0 +1,1 @@
+done
Index: tests/array-container/.expect/language-dim-mismatch.txt
===================================================================
--- tests/array-container/.expect/language-dim-mismatch.txt	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
+++ tests/array-container/.expect/language-dim-mismatch.txt	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -0,0 +1,10 @@
+array-container/language-dim-mismatch.cfa:12:1 error: Type argument given for value parameter: instance of struct SN with body 1
+... with parameters
+  float
+
+array-container/language-dim-mismatch.cfa:13:1 error: Expression argument given for type parameter: instance of struct ST with body 1
+... with parameters
+  constant expression (42 42: signed int)
+  with resolved type:
+    signed int
+
Index: tests/array-container/array-basic.cfa
===================================================================
--- tests/array-container/array-basic.cfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ tests/array-container/array-basic.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -61,17 +61,17 @@
 forall( [Nw], [Nx], [Ny], [Nz] )
 void fillHelloData( array( float, Nw, Nx, Ny, Nz ) & wxyz ) {
-    for (w; z(Nw))
-    for (x; z(Nx))
-    for (y; z(Ny))
-    for (z; z(Nz))
+    for (w; Nw)
+    for (x; Nx)
+    for (y; Ny)
+    for (z; Nz)
         wxyz[w][x][y][z] = getMagicNumber(w, x, y, z);
 }
 
-forall( [Zn]
+forall( [N]
       , S & | sized(S)
       )
-float total1d_low( arpk(Zn, S, float, float ) & a ) {
+float total1d_low( arpk(N, S, float, float ) & a ) {
     float total = 0.0f;
-    for (i; z(Zn))
+    for (i; N)
         total += a[i];
     return total;
@@ -98,5 +98,5 @@
 
     expect = 0;
-    for (i; z(Nw))
+    for (i; Nw)
         expect += getMagicNumber( i, slice_ix, slice_ix, slice_ix );
     printf("expect Ws             = %f\n", expect);
@@ -117,5 +117,5 @@
 
     expect = 0;
-    for (i; z(Nx))
+    for (i; Nx)
         expect += getMagicNumber( slice_ix, i, slice_ix, slice_ix );
     printf("expect Xs             = %f\n", expect);
Index: tests/array-container/array-md-sbscr-cases.cfa
===================================================================
--- tests/array-container/array-md-sbscr-cases.cfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ tests/array-container/array-md-sbscr-cases.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -20,8 +20,8 @@
 forall( [Nw], [Nx], [Ny], [Nz] )
 void fillHelloData( array( float, Nw, Nx, Ny, Nz ) & wxyz ) {
-    for (w; z(Nw))
-    for (x; z(Nx))
-    for (y; z(Ny))
-    for (z; z(Nz))
+    for (w; Nw)
+    for (x; Nx)
+    for (y; Ny)
+    for (z; Nz)
         wxyz[w][x][y][z] = getMagicNumber(w, x, y, z);
 }
@@ -246,25 +246,25 @@
     assert(( wxyz[[2,  3,  4,  5]] == valExpected ));
 
-    for ( i; z(Nw) ) {
+    for ( i; Nw ) {
         assert(( wxyz[[ i, 3, 4, 5 ]] == getMagicNumber(i, 3, 4, 5) ));
     }
 
-    for ( i; z(Nx) ) {
+    for ( i; Nx ) {
         assert(( wxyz[[ 2, i, 4, 5 ]] == getMagicNumber(2, i, 4, 5) ));
     }
 
-    for ( i; z(Ny) ) {
+    for ( i; Ny ) {
         assert(( wxyz[[ 2, 3, i, 5 ]] == getMagicNumber(2, 3, i, 5) ));
     }
 
-    for ( i; z(Nz) ) {
+    for ( i; Nz ) {
         assert(( wxyz[[ 2, 3, 4, i ]] == getMagicNumber(2, 3, 4, i) ));
     }
 
-    for ( i; z(Nw) ) {
+    for ( i; Nw ) {
         assert(( wxyz[[ i, all, 4, 5 ]][3] == getMagicNumber(i, 3, 4, 5) ));
     }
 
-    for ( i; z(Nw) ) {
+    for ( i; Nw ) {
         assert(( wxyz[[ all, 3, 4, 5 ]][i] == getMagicNumber(i, 3, 4, 5) ));
     }
Index: tests/array-container/language-dim-mismatch.cfa
===================================================================
--- tests/array-container/language-dim-mismatch.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
+++ tests/array-container/language-dim-mismatch.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -0,0 +1,15 @@
+forall( [N] )
+struct SN {};
+
+forall( T )
+struct ST {};
+
+int main() {
+
+    SN(42) good1;
+    ST(float) good2;
+
+    SN(float) bad1;  // first  expected error: Type argument given for value parameter
+    ST(42) bad2;     // second expected error: Expression argument given for type parameter
+
+}
Index: tests/device/cpu.cfa
===================================================================
--- tests/device/cpu.cfa	(revision 6992f95a981e8e0eb21dbaf2cd2f1257510f67f2)
+++ tests/device/cpu.cfa	(revision e319fc52866340c9ab388498d1f0e9aec5c25fc3)
@@ -17,9 +17,129 @@
 #include <fstream.hfa>
 #include <device/cpu.hfa>
+#include <stdlib.hfa>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
 extern "C" {
+	#include <dirent.h>
+	#include <sys/types.h>
+	#include <sys/stat.h>
 	#include <sys/sysinfo.h>
+	#include <fcntl.h>
+}
+
+// go through a directory calling fn on each file
+static int iterate_dir( const char * path, void (*fn)(struct dirent * ent) ) {
+	// open the directory
+	DIR *dir = opendir(path);
+	if(dir == 0p) { return ENOTDIR; }
+
+	// call fn for each
+	struct dirent * ent;
+	while ((ent = readdir(dir)) != 0p) {
+		fn( ent );
+	}
+
+	// no longer need this
+	closedir(dir);
+	return 0;
+}
+
+// count the number of directories with the specified prefix
+// the directories counted have the form '[prefix]N' where prefix is the parameter
+// and N is an base 10 integer.
+static int count_prefix_dirs(const char * path, const char * prefix) {
+	// read the directory and find the cpu count
+	// and make sure everything is as expected
+	int max = -1;
+	int count = 0;
+	void lambda(struct dirent * ent) {
+		// were are looking for prefixX, where X is a number
+		// check that it starts with 'cpu
+		char * s = strstr(ent->d_name, prefix);
+		if(s == 0p) { return; }
+		if(s != ent->d_name) { return; }
+
+		// check that the next part is a number
+		s += strlen(prefix);
+		char * end;
+		long int val = strtol(s, &end, 10);
+		if(*end != '\0' || val < 0) { return; }
+
+		// check that it's a directory
+		if(ent->d_type != DT_DIR) { return; }
+
+		// it's a match!
+		max = max(val, max);
+		count++;
+	}
+	iterate_dir(path, lambda);
+
+	/* paranoid */ verifyf(count == max + 1, "Inconsistent %s count, counted %d, but max %s was %d", prefix, count, prefix, (int)max);
+
+	return count;
+}
+
+// Count number of cache *indexes* in the system
+// cache indexes are distinct from cache level as Data or Instruction cache
+// can share a level but not an index
+// PITFALL: assumes all cpus have the same indexes as cpu0
+static int count_cache_indexes(void) {
+	return count_prefix_dirs("/sys/devices/system/cpu/cpu0/cache", "index");
+}
+
+// read information about a spcficic cache index/cpu file into the output buffer
+static size_t read_cpuidxinfo_into(unsigned cpu, unsigned idx, const char * file, char * out, size_t out_len) {
+	// Pick the file we want and read it
+	char buf[128];
+	/* paranoid */ __attribute__((unused)) int len =
+	snprintf(buf, 128, "/sys/devices/system/cpu/cpu%u/cache/index%u/%s", cpu, idx, file);
+	/* paranoid */ verifyf(len > 0, "Could not generate '%s' filename for cpu %u, index %u", file, cpu, idx);
+
+	int fd = open(buf, 0, O_RDONLY);
+	/* paranoid */ verifyf(fd > 0, "Could not open file '%s'", buf);
+
+	ssize_t r = read(fd, out, out_len);
+	/* paranoid */ verifyf(r > 0, "Could not read file '%s'", buf);
+
+	/* paranoid */ __attribute__((unused)) int ret =
+	close(fd);
+	/* paranoid */ verifyf(ret == 0, "Could not close file '%s'", buf);
+
+	out[r-1] = '\0';
+	return r-1;
+}
+
+unsigned find_idx() {
+	int idxs = count_cache_indexes();
+
+	unsigned found_level = 0;
+	unsigned found = -1u;
+	for(i; idxs) {
+		unsigned idx = idxs - 1 - i;
+		char buf[32];
+
+		// Level is the cache level: higher means bigger and slower
+		read_cpuidxinfo_into(0, idx, "level", buf, 32);
+		char * end;
+		unsigned long level = strtoul(buf, &end, 10);
+		/* paranoid */ verifyf(level <= 250, "Cpu %u has more than 250 levels of cache, that doesn't sound right", 0);
+		/* paranoid */ verify(*end == '\0');
+
+		if(found_level < level) {
+			found_level = level;
+			found = idx;
+		}
+	}
+
+	/* paranoid */ verify(found != -1u);
+	return found;
 }
 
 int main() {
+	//-----------------------------------------------------------------------
 	int ret1 = get_nprocs();
 	int ret2 = cpu_info.hthrd_count;
@@ -31,3 +151,48 @@
 	}
 
+	//-----------------------------------------------------------------------
+	// Make sure no one has the same self
+	for(ime; cpu_info.hthrd_count) {
+		unsigned me = cpu_info.llc_map[ime].self;
+		{
+			unsigned s = cpu_info.llc_map[ime].start;
+			unsigned e = s + cpu_info.llc_map[ime].count;
+			if(me < s || me >= e) {
+				sout | "CPU" | ime | "outside of it's own map: " | s | "<=" | me | "<" | e;
+			}
+		}
+
+
+		for(ithem; cpu_info.hthrd_count) {
+			if(ime == ithem) continue;
+
+			unsigned them = cpu_info.llc_map[ithem].self;
+			if(me == them) {
+				sout | "CPU" | ime | "has conflicting self id with" | ithem | "(" | me | ")";
+			}
+		}
+	}
+
+
+	//-----------------------------------------------------------------------
+	unsigned idx = find_idx();
+	// For all procs check mapping is consistent
+	for(cpu_me; cpu_info.hthrd_count) {
+		char buf_me[32];
+		size_t len_me = read_cpuidxinfo_into(cpu_me, idx, "shared_cpu_list", buf_me, 32);
+		for(cpu_them; cpu_info.hthrd_count) {
+			if(cpu_me == cpu_them) continue;
+			char buf_them[32];
+			size_t len_them = read_cpuidxinfo_into(cpu_them, idx, "shared_cpu_list", buf_them, 32);
+
+			bool match_file = len_them == len_me && 0 == strncmp(buf_them, buf_me, len_me);
+			bool match_info = cpu_info.llc_map[cpu_me].start == cpu_info.llc_map[cpu_them].start && cpu_info.llc_map[cpu_me].count == cpu_info.llc_map[cpu_them].count;
+
+			if(match_file != match_info) {
+				sout | "CPU" | cpu_me | "and" | cpu_them | "have inconsitent file and cpu_info";
+				sout | cpu_me | ": <" | cpu_info.llc_map[cpu_me  ].start | "," | cpu_info.llc_map[cpu_me  ].count | "> '" | buf_me   | "'";
+				sout | cpu_me | ": <" | cpu_info.llc_map[cpu_them].start | "," | cpu_info.llc_map[cpu_them].count | "> '" | buf_them | "'";
+			}
+		}
+	}
 }
