ArrayView, Sizing-time config vars (samplerate), timebase for ringbuffers, fix bug with sizing variant tuples (tuple tail was ignored!)
M .hgsubstate +2 -2
@@ 1,5 1,5 @@ 
-c1bcc6e5a506ee98223a58316481e1b775153c02 editors/st3
-e7b9b9118a609b6b283ee8d6845a16b08079f649 library
+6b1b48369d48250bfea95217cd08253cc5b43d71 editors/st3
+c7e2fdabbb72b80f6d1f880eda7956cd0f517546 library
 19292d513d74939f058a037bbc5ea0ccd6acbc98 src/lithe
 c3e562558279e535531dd37f3c2b2770e49000d9 src/pad
 adf19f7ab6aa4d8bac58329c887eb210b1464f3d src/paf

          
M editors/kronos.natvis +2 -1
@@ 113,7 113,7 @@ 
   <Type Name="K3::Nodes::SubroutineArgument">
     <DisplayString>ARG{ID}</DisplayString>
   </Type>
-  <Type Name="K3::Nodes::Monad">
+  <Type Name="K3::Nodes::Deps">
     <DisplayString>Proc( {*upstream[0]} )</DisplayString>
     <Expand>
       <Item Name="Value">*upstream[0]</Item>

          
@@ 220,6 220,7 @@ 
       <Item Name="Source">*upstream[1]</Item>
       <Item Name="Size">*upstream[2]</Item>
       <Item Name="Clock" Condition="reactivity != nullptr">*reactivity</Item>
+      <Item Name="Repeat">*upstream[3]</Item>
     </Expand>
   </Type>
   <Type Name="K3::Nodes::Boundary">

          
M src/backends/BinaryenCompiler.cpp +8 -2
@@ 17,6 17,7 @@ namespace K3 {
 		else if (t.IsFloat64()) return xfm.Float64Ty();
 		else if (t.IsInt32()) return xfm.Int32Ty();
 		else if (t.IsInt64()) return xfm.Int64Ty();
+		else if (t.IsArrayView()) return xfm.PtrTy();
 		INTERNAL_ERROR("Illegal unlowered type in generic codegen");
 	}
 

          
@@ 125,7 126,7 @@ namespace K3 {
 			return xfm->SizeOfPointer();
 		}
 
-		CODEGEN_EMIT(Monad) {
+		CODEGEN_EMIT(Deps) {
 			return xfm(GetUp(0));
 		}
 

          
@@ 188,9 189,14 @@ namespace K3 {
 		CODEGEN_EMIT(Buffer) {
 			switch (alloc) {
 			case Stack:
+			case StackZeroed:
 				{
 					auto sz{ xfm(GetUp(0)) };
-					return xfm->Alloca(sz, alignment);
+					auto buf =  xfm->Alloca(sz, alignment);
+					if (alloc == StackZeroed) {
+						xfm->MemSet(buf, xfm->Const(0), sz);
+					}
+					return buf;
 				}
 			case Module:
 				{

          
M src/backends/BinaryenEmitter.h +1 -0
@@ 181,6 181,7 @@ namespace K3 {
 			}
 
 			BinaryenExpressionRef Const(PortableSize psz) {
+				assert(0 && "add config slots");
 				return Const((int)(psz.byteCount + psz.pointerCount * 4));
 			}
 

          
M src/backends/CodeGenCompiler.cpp +83 -9
@@ 134,7 134,7 @@ namespace K3 {
 			Buffer *b;
 			if (node->Cast(b) && b->GetAllocation() == Buffer::Stack) return true;
 
-			if (IsOfExactType<Monad>(node) ||
+			if (IsOfExactType<Deps>(node) ||
 				IsOfExactType<Offset>(node) ||
 				IsOfExactType<Dereference>(node) ||
 				IsOfExactType<Reference>(node)) {

          
@@ 191,16 191,16 @@ namespace K3 {
 			assert(free.empty() == false); // assume at least one schedulable node
 
 	#ifndef NDEBUG
-			unsigned numMonads(0);
+			unsigned numDepsNodes = 0;
 	#endif
 			while (free.empty() == false) {
 				const SchedulingUnit *next = *free.begin();
 				free.erase(free.begin());
-				if (IsOfExactType<Monad>(get<0>(*next)) == false)
+				if (IsOfExactType<Deps>(get<0>(*next)) == false)
 					schedule.push_back(*next);
 	#ifndef NDEBUG
 				else
-					numMonads++;
+					numDepsNodes++;
 	#endif
 
 				auto deps(edges.equal_range(get<0>(*next)));

          
@@ 210,18 210,49 @@ namespace K3 {
 				}
 			}
 	#ifndef NDEBUG
-			assert(schedule.size() + numMonads == units.size() && "Unscheduled nodes found");
+			assert(schedule.size() + numDepsNodes == units.size() && "Unscheduled nodes found");
 	#endif
 			return schedule;
 		}
 	}
 
 	PortableSize operator+(PortableSize a, PortableSize b) {
-		return { a.pointerCount + b.pointerCount, a.byteCount + b.byteCount };
+		a.pointerCount += b.pointerCount;
+		a.byteCount += b.byteCount;
+		for (auto &slot : b.configSlotCount) {
+			a.configSlotCount[slot.first] += slot.second;
+		}
+		return a;
 	}
 
 	PortableSize operator*(PortableSize a, int b) {
-		return { a.pointerCount * b, a.byteCount * b };
+		a.pointerCount *= b;
+		a.byteCount *= b;
+		for (auto &slot : a.configSlotCount) {
+			slot.second *= b;
+		}
+		return a;
+	}
+
+	PortableSize operator>>(PortableSize a, int b) {
+		a.pointerCount >>= b;
+		a.byteCount >>= b;
+		for (auto &slot : a.configSlotCount) {
+			slot.second >>= b;
+		}
+		return a;
+	}
+
+	PortableSize Max(PortableSize a, PortableSize b) {
+		if (b.pointerCount > a.pointerCount) a.pointerCount = b.pointerCount;
+		if (b.byteCount > a.byteCount) a.byteCount = b.byteCount;
+		for (auto & slot : a.configSlotCount) {
+			slot.second = std::max(slot.second, b.configSlotCount[slot.first]);
+		}
+		for (auto & slot : b.configSlotCount) {
+			a.configSlotCount[slot.first] = std::max(slot.second, a.configSlotCount[slot.first]);
+		}
+		return a;
 	}
 
 	PortableSize SizingTransform::operate(CTRef n) {

          
@@ 229,9 260,10 @@ namespace K3 {
 		Offset* o;
 		SubroutineArgument *a;
 		SubroutineStateAllocation *sra;
-		Monad *mn;
+		Deps *mn;
 		SubroutineMeta* mt;
 		SizeOfPointer* szp;
+		Copy* cpy;
 		Native::Constant* c;
 
 		if (n->Cast(mn) || n->Cast(mt)) {

          
@@ 258,7 290,7 @@ namespace K3 {
 		}
 		if (n->Cast(a)) {
 			assert(a->IsState());
-			return { 0,0 };
+			return { 0, 0 };
 		}
 		if (n->Cast(c)) {
 			if (c->FixedResult().IsInt32()) {

          
@@ 271,6 303,48 @@ namespace K3 {
 		if (n->Cast(szp)) {
 			return { 1, 0 };
 		}
+		if (auto bin = n->Cast<Native::ITypedBinary>()) {
+			auto a = (*this)(n->GetUp(0));
+			auto b = (*this)(n->GetUp(1));
+			switch (bin->GetOpcode()) {
+			case Native::Mul:
+				assert(b.pointerCount == 0);
+				assert(b.configSlotCount.empty());
+				return a * b.byteCount;
+			case Native::Add:
+				return a + b;
+			case Native::Max:
+				return Max(a, b);
+			case Native::BitShiftRight:
+				assert(b.pointerCount == 0);
+				assert(b.configSlotCount.empty());
+				return a >> b.byteCount;
+				break;
+			default:
+				KRONOS_UNREACHABLE;
+			}
+		}
+		if (auto un = n->Cast<Native::ITypedUnary>()) {
+			auto a = (*this)(n->GetUp(0));
+			switch(un->GetOpcode()) {
+			case Native::ToInt64:
+			case Native::ToInt32:
+				return a;
+			default:
+				KRONOS_UNREACHABLE;
+			}
+		}
+		if (auto cfg = n->Cast<Configuration>()) {
+			PortableSize psz{ 0,0 };
+			psz.configSlotCount[cfg->GetSlotIndex()] = 1 << 24;
+			return psz;
+		}
+		if (n->Cast<Dereference>()) {
+			return (*this)(n->GetUp(0));
+		}
+		if (n->Cast(cpy)) {
+			return (*this)(cpy->GetUp(1));
+		}
 		KRONOS_UNREACHABLE;
 	}
 }
  No newline at end of file

          
M src/backends/CodeGenCompiler.h +1 -0
@@ 63,6 63,7 @@ namespace K3 {
 	struct PortableSize {
 		int64_t pointerCount;
 		int64_t byteCount;
+		std::unordered_map<int, int64_t> configSlotCount;
 	};
 
 	PortableSize operator*(PortableSize, int);

          
M src/backends/CodeMotionPass.cpp +2 -2
@@ 273,11 273,11 @@ namespace K3 {
 		}
 
 		Typed* CodeMotionPass::materializeVariables(CodeMotionPass& cmp, Typed* call) {
-			Monad *variableMaterializer(0);
+			Deps *variableMaterializer(0);
 			for (auto var : cmp) {
 				if (materializeAll || variableBoundaries.find(var) != variableBoundaries.end()) {
 					if (variableMaterializer == 0) {
-						variableMaterializer = Monad::New();
+						variableMaterializer = Deps::New();
 						variableMaterializer->Connect(call->GetUp(0));
 					}
 					variableMaterializer->Connect(SetGlobalVariable::New(var, var));

          
M src/backends/CopyElision.cpp +1 -1
@@ 83,7 83,7 @@ namespace K3 {
 			//sfx.SetArgumentElision(sfx.elision);
 		}
 
-		void Monad::CopyElision(Backends::CopyElisionTransform& sfx) const
+		void Deps::CopyElision(Backends::CopyElisionTransform& sfx) const
 		{
 			if (GetNumCons()) sfx(GetUp(0));
 		}

          
M src/backends/LLVMCompiler.cpp +79 -20
@@ 99,7 99,21 @@ namespace K3 {
 
 		llvm::Value* LLVMTransform::ReifySize(llvm::IRBuilder<>& b, PortableSize ps) {
 			auto ptrSize = b.CreatePtrToInt(b.CreateConstGEP1_32(llvm::Constant::getNullValue(b.getInt8PtrTy()->getPointerTo()), ps.pointerCount), b.getInt64Ty());
-			return b.CreateAdd(ConstantInt::get(b.getInt64Ty(), ps.byteCount), ptrSize);
+			auto sumSize = b.CreateAdd(ConstantInt::get(b.getInt64Ty(), ps.byteCount), ptrSize);
+			auto M = b.GetInsertBlock()->getParent()->getParent();
+			auto cfg = M->getOrInsertFunction(
+				"GetConfigurationSlot", 
+				llvm::FunctionType::get(b.getInt8PtrTy(), { b.getInt32Ty() }, false));
+//			return new LLVMSignal(lt.GetBuilder().CreateCall(fn, { lt.GetBuilder().getInt32(slotIndex) }));
+			for (auto &slot : ps.configSlotCount) {
+				auto cfgPtr = b.CreateCall(cfg, { b.getInt32(slot.first) });
+				llvm::Value* slotVal = b.CreateLoad(b.CreatePointerCast(cfgPtr, b.getFloatTy()->getPointerTo()));
+				slotVal = b.CreateFPToSI(slotVal, b.getInt64Ty());
+				slotVal = b.CreateMul(slotVal, b.getInt64(slot.second));
+				slotVal = b.CreateLShr(slotVal, 24);
+				sumSize = b.CreateAdd(sumSize, slotVal);
+			}
+			return sumSize;
 		}
 
 		LLVMTransform::LLVMTransform(ILLVMCompilationPass &comp) : CachedTransform(0), compilation(comp), bb(BasicBlock::Create(comp.GetContext(), "Top", function)), builder(bb),

          
@@ 517,17 531,31 @@ namespace K3 {
 			else return buf;
 		}
 
+		Ref<LLVMSignal> Configuration::Compile(LLVMTransform& lt, ActivityMaskVector *active) const {
+/*			auto var = dyn_cast<GlobalVariable>(lt.GetModule()->getOrInsertGlobal("cfg_" + std::to_string(slotIndex), lt.GetType(ty)));
+			var->setLinkage(GlobalValue::AvailableExternallyLinkage);
+			return new LLVMSignal(lt.GetBuilder().CreateBitCast(var,lt.GetBuilder().getInt8PtrTy()));*/
+			auto fn = lt.GetModule()->getOrInsertFunction("GetConfigurationSlot", llvm::FunctionType::get(lt.GetBuilder().getInt8PtrTy(), { lt.GetBuilder().getInt32Ty() }, false));
+			return new LLVMSignal(lt.GetBuilder().CreateCall(fn, { lt.GetBuilder().getInt32(slotIndex) }));
+		}
+
 		Ref<LLVMSignal> GetSlot::Compile(LLVMTransform& lt, ActivityMaskVector* active) const {
 			auto &b{ lt.GetBuilder() };
 			auto self = lt.GetParameter(1);
 			self = b.CreateBitCast(self, b.getInt8PtrTy()->getPointerTo(), "slots");
 			auto ptrPtr = b.CreateConstGEP1_32(self, index, "slot");
 			llvm::Value* ptr = b.CreateLoad(ptrPtr);
-			if (lt.IsInitPass() && GetNumCons()) {
-				llvm::Value* init = *lt(GetUp(0));
-				ptr = b.CreateSelect(
-					b.CreateICmpNE(ptr, Constant::getNullValue(ptr->getType())), ptr, init);
-				b.CreateStore(ptr, ptrPtr);
+			if (lt.IsInitPass()) {
+				if (GetUp(0)) {
+					llvm::Value* init = *lt(GetUp(0));
+					ptr = b.CreateSelect(
+						b.CreateICmpNE(ptr, Constant::getNullValue(ptr->getType())), ptr, init);
+					b.CreateStore(ptr, ptrPtr);
+				} else {
+					auto fn = lt.GetModule()->getOrInsertFunction("GetConfigurationSlot", 
+																  llvm::FunctionType::get(lt.GetBuilder().getInt8PtrTy(), { lt.GetBuilder().getInt32Ty() }, false));
+					return new LLVMSignal(lt.GetBuilder().CreateCall(fn, { lt.GetBuilder().getInt32(index) }));
+				}
 			}
 			return new LLVMSignal(ptr);
 		}

          
@@ 770,10 798,10 @@ namespace K3 {
 			return new LLVMSignal(b.CreatePtrToInt(onePtr, b.getInt64Ty()));
 		}
 
-		Ref<LLVMSignal> Monad::Compile(LLVMTransform& lt, ActivityMaskVector* active) const {
+		Ref<LLVMSignal> Deps::Compile(LLVMTransform& lt, ActivityMaskVector* active) const {
 			auto a(lt(GetUp(0)));
 			//if (lt.IsInitPass()) {f
-			//	// init pass doesn't sort nodes, so monadic deps must be honred
+			//	// init pass doesn't sort nodes, so Depsic deps must be honred
 			//	for(unsigned i(GetNumCons()-1);i>0;--i) lt(GetUp(i));
 			//}
 			return a;

          
@@ 819,21 847,44 @@ namespace K3 {
 					llvm::Value *dst(*dstSig), *src(*srcSig), *elSz(*szSig);
 					dst = b.CreateBitCast(dst, b.getInt8PtrTy());
 					auto align = srcAlign && dstAlign ? AlignPowerOf2(-((-srcAlign) | (-dstAlign))) : 4;
-					Ref<LLVMSignal> fst = new LLVMSignal(b.CreateMemCpy(dst, src, elSz, align));
-					while (offset < repeat) {
-						unsigned cpy = std::min(offset, unsigned(repeat - offset));
-						auto sz(b.CreateMul(b.getInt32(cpy), elSz));
-						auto subdst(b.CreateGEP(dst, b.CreateMul(b.getInt32(offset), elSz)));
-						b.CreateMemCpy(subdst, dst, sz, align);
-						offset += cpy;
+
+					// don't emit empty loops
+					int staticLoopCount = 999;
+					if (auto c = GetUp(3)->Cast<Native::Constant>()) {
+						if (staticLoopCount = *(int32_t*)c->GetPointer() < 1) {
+							break;
+						}
 					}
+
+					if (staticLoopCount > 1) {
+						auto loopCount = b.CreateSExt(*lt(GetUp(3), active), b.getInt64Ty());
+
+						auto priorBlock = b.GetInsertBlock();
+						auto loopBlock = llvm::BasicBlock::Create(b.getContext(), "initLoop", priorBlock->getParent());
+						auto nextBlock = llvm::BasicBlock::Create(b.getContext(), "endLoop", priorBlock->getParent());
+						b.CreateBr(loopBlock);
+						b.SetInsertPoint(loopBlock);
+
+						auto i = b.CreatePHI(b.getInt64Ty(), 2, "i");
+						auto nextI = b.CreateAdd(i, b.getInt64(1));
+						auto iDst = b.CreateGEP(dst, b.CreateMul(i, elSz));
+						i->addIncoming(b.getInt64(0), priorBlock);
+						i->addIncoming(nextI, loopBlock);
+						b.CreateMemCpy(b.CreateGEP(dst, b.CreateMul(i, elSz)), src, elSz, align);
+						b.CreateCondBr(b.CreateICmpNE(nextI, loopCount), loopBlock, nextBlock);
+
+						b.SetInsertPoint(nextBlock);
+					} else {
+						b.CreateMemCpy(dst, src, elSz, align);
+					}
+
 					break;
 				}
 				default:
 					INTERNAL_ERROR("Bad copy mode in LLVM backend");
 				}
 			}
-			return Ref<LLVMSignal>();
+			return dstSig;
 		}
 
 		static llvm::Value* IsBufferBig(llvm::IRBuilder<>& b, llvm::Value* size) {

          
@@ 852,12 903,18 @@ namespace K3 {
 			switch (alloc) {
 			case Empty:
 				return new LLVMSignal(llvm::Constant::getNullValue(lt.GetBuilder().getInt8PtrTy()));
+			case StackZeroed:
 			case Stack:
-				{
-					auto sz = lt(GetUp(0));
-					auto allocaed = lt.Alloca(GUID, *sz, alignment);
-					return new LLVMSignal(allocaed);
+			{
+				auto sz = lt(GetUp(0));
+				auto allocaed = lt.Alloca(GUID, *sz, alignment);
+
+				if (alloc == StackZeroed) {
+					lt.GetBuilder().CreateMemSet(allocaed, lt.GetBuilder().getInt8(0), *sz, alignment);
 				}
+
+				return new LLVMSignal(allocaed);
+			}
 			case Module:
 			{
 				Native::Constant *c(0);

          
@@ 937,6 994,8 @@ namespace K3 {
 				return new LLVMSignal(b.CreateBitOrPointerCast(*value, b.getInt32Ty()));
 			} else if (to.IsInt64()) {
 				return new LLVMSignal(b.CreateBitOrPointerCast(*value, b.getInt64Ty()));
+			} else if (to.IsArrayView()) {
+				return new LLVMSignal(b.CreateIntToPtr(*value, b.getInt8PtrTy()));
 			}
 			assert(0 && "unknown native type");
 			KRONOS_UNREACHABLE;

          
M src/backends/LLVMModule.cpp +38 -55
@@ 222,9 222,6 @@ namespace K3 {
 				}
 			}
 
-			auto connectStub = ir.API(ir.declare<PlumbTy>(Linkage::Export, "Connect"));
-			auto disconnectStub = ir.API(ir.declare<PlumbTy>(Linkage::Export, "Disconnect"));
-
 			auto initStub = ir.API(ir.defn<krt_constructor_call>(Linkage::Export, "Initialize", [&](auto stub, auto args) {
 				size_t asz(this->GetArgumentType().GetSize()),
 					rsz(this->GetResultType().GetSize());

          
@@ 235,7 232,6 @@ namespace K3 {
 				auto instance = (llvm::Argument*)ai++;
 				auto self_offset(stub.CreateGEP(instance, stub.CreateCall(sizeOfStateStub, { stub.getInt64(0) }, "sizeof_state")));
 
-				auto plumbingHost = (llvm::Argument*)ai++;
 				auto argumentData = (llvm::Argument*)ai++;
 
 				if (asz) {

          
@@ 246,7 242,6 @@ namespace K3 {
 							this->GetArgumentIndex()));
 				}
 
-				stub.CreateCall(connectStub, { instance, plumbingHost });
 				stub.CreateCall(initFunc, { self_offset, instance, argumentData, outputBuf }, "init")->setCallingConv(CallingConv::Fast);
 				stub.CreateRetVoid();
 			}));

          
@@ 256,7 251,6 @@ namespace K3 {
 				auto ai = args.begin();
 				auto instance = (llvm::Argument*)ai++;
 				auto host = (llvm::Argument*)ai++;
-				stub.CreateCall(disconnectStub, { instance, host });
 				stub.CreateRetVoid();
 			}));
 

          
@@ 321,21 315,12 @@ namespace K3 {
 						GlobalVarType::External,
 						std::make_pair(1, 1),
 						K3::Type::Nil
-									});
+					});
 				}
 			}
 
-			auto doNothing = ir.defn<krt_plumbing_callback>(Linkage::Internal, "DoNothing", [&](auto stub, auto args) {
-//				typedef void(*krt_plumbing_callback)(krt_host, krt_instance, const struct krt_sym*, void const** slot);
-				auto ai = args.begin();
-				ai++; ai++; ai++;
-				stub.CreateStore(ir.nullConstant<void*>(), ai);
-				stub.CreateRetVoid();
-			});
-
-			doNothing->setCallingConv(CallingConv::C);
-
 			methods.erase(Type::Pair(Type("unsafe"), Type("accumulator")));
+			int maxNoDefaultSlot = -1;
 			for (auto& gv : methods) {
 				std::stringstream sym;
 				sym << gv.first;

          
@@ 354,21 339,24 @@ namespace K3 {
 					auto slotI = globalSymbolTable.find(gv.second.uid);
 					auto slotIndex = slotI != globalSymbolTable.end() ? std::int32_t(slotI->second) : -1;
 
-					std::int8_t defaultValStatus =
-						(gv.second.varType == External &&
-						 globalKeyTable.find(gv.first) != globalKeyTable.end()) ? 1 : 0;
+					std::int32_t noDefaultVal =
+						((gv.second.varType == External || 
+						  gv.second.varType == Configuration) 
+						 && globalKeyTable.find(gv.first) != globalKeyTable.end()) ? 1 : 0;
 
 					symTableEntry.emplace_back(
 						ConstantStruct::get(symTy, {
 							ir.constant(sym.str()),
 							ir.constant(descr.str()),
-							doNothing,
 							triggerCallback,
-							doNothing,
 							ir.constant(std::int64_t(gv.second.data.GetSize())),
 							ir.constant(slotIndex),
-							ir.constant(defaultValStatus)
-											}));
+							ir.constant(noDefaultVal)
+						}));
+
+					if (noDefaultVal && slotIndex > maxNoDefaultSlot) {
+						maxNoDefaultSlot = slotIndex;
+					}
 				}
 			}
 

          
@@ 379,19 367,44 @@ namespace K3 {
 			classTy->setBody({
 				KRT_CLASS_SPEC(COMMA),
 				ArrayType::get(symTy, symTableEntry.size()),
-							 }, true);
+			}, true);
 #undef COMMA
 #undef F
 
 			auto symTableTy = ArrayType::get(symTy, symTableEntry.size());
 			auto symTableInit = llvm::ConstantArray::get(symTableTy, symTableEntry);
 
+			auto initTableTy = ArrayType::get(ty.ptr, maxNoDefaultSlot + 1);
+			auto initTableInit = llvm::ConstantArray::getNullValue(initTableTy);
+
+			auto slotInitializerData = ir.def(Linkage::Internal, initTableTy, "ExternalInit", false);
+			slotInitializerData->setInitializer(initTableInit);
+
+			ir.implement((llvm::Function*)M->getOrInsertFunction("GetConfigurationSlot", TypeResolver<void*(std::int32_t)>::get(ir)),
+							[&](IRBuilder<>& stub, auto args) {
+				auto ai = args.begin();
+				auto index = (llvm::Argument*)ai++;
+				auto slotPtr = stub.CreateGEP(slotInitializerData, { stub.getInt32(0), index });
+				stub.CreateRet(stub.CreateLoad(slotPtr));
+			});
+
+			auto configureCall = ir.API(ir.defn<krt_configure_call>(Linkage::Export, "SetConfigurationSlot",
+																	[&](IRBuilder<>& stub, auto args) {
+				auto ai = args.begin();
+				auto index = (llvm::Argument*)ai++;
+				auto data = (llvm::Argument*)ai++;
+				auto slotPtr = stub.CreateGEP(slotInitializerData, { stub.getInt32(0), index });
+				stub.CreateStore(data, slotPtr);
+				stub.CreateRetVoid();
+			}));
+
 			std::stringstream evalArg, resultTy;
 			GetArgumentType().OutputJSONTemplate(evalArg, false);
 			GetResultType().OutputJSONTemplate(resultTy, false);
 
 			auto classInit = ConstantStruct::get(
 				classTy, {
+					configureCall,
 					sizeOfStub,
 					initStub,
 					getValStub,

          
@@ 410,36 423,6 @@ namespace K3 {
 			auto classData = ir.def(Linkage::Export, classTy, "Class", false);
 			classData->setInitializer(classInit);
 
-			auto makePlumbingFunc = [&](llvm::Function* fn, const char* name, bool onOff) {
-				ir.implement(fn, [&](IRBuilder<>& stub, auto args) {
-					auto ai = args.begin();
-					auto self = (llvm::Argument*)ai++;
-					auto env = (llvm::Argument*)ai++;
-
-					for (int i = 0; i < symTableEntry.size(); ++i) {
-						auto symTable = stub.CreateConstGEP2_32(nullptr, classData, 0, 12);
-						auto symStructPtr = stub.CreateConstGEP2_32(nullptr, symTable, 0, i);
-						auto symStruct = stub.CreateLoad(symStructPtr);
-						auto plumbCall = stub.CreateExtractValue(symStruct, { onOff ? 2u : 4u }, name);
-						auto slotIdx = stub.CreateExtractValue(symStruct, { 6 }, "idx");
-
-						auto offset = stub.CreateCall(sizeOfStateStub, { stub.CreateZExt(slotIdx, ty.i64) });
-						auto slotPtr =
-							stub.CreateSelect(stub.CreateICmpSLT(slotIdx, ir.nullConstant<int32_t>()),
-								ir.nullConstant<void**>(),
-								stub.CreateBitCast(stub.CreateGEP(self, offset), stub.getInt8PtrTy()->getPointerTo()));
-
-						auto callInstr = stub.CreateCall(plumbCall, { env, self, symStructPtr, slotPtr });
-						callInstr->setCallingConv(CallingConv::C);
-					}
-
-					stub.CreateRetVoid();
-				});
-			};
-
-			makePlumbingFunc(connectStub, "Connect", true);
-			makePlumbingFunc(disconnectStub, "Disconnect", false);
-
 			GetModule()->getFunction("__launch")->removeFromParent();
 #ifndef NDEBUG
 			std::string errstr;

          
M src/backends/LLVMSignal.h +4 -4
@@ 9,7 9,7 @@ namespace llvm{
 
 namespace K3 {
 	namespace Nodes{
-		class Monad;
+		class Deps;
 	};
 
 	namespace Backends{

          
@@ 23,9 23,9 @@ namespace K3 {
 			Ref<LLVMSignal> Reference();
 			Ref<LLVMSignal> Dereference(llvm::Value *dereferencedValue);
 			operator llvm::Value*() {return val;}
-			operator const llvm::Value*() const {return val;}
-#ifndef NDEBUG
-            int offsetCounter = 0;
+			operator const llvm::Value*() const {return val;}
+#ifndef NDEBUG
+            int offsetCounter = 0;
 #endif
 		};
 	};

          
M src/backends/SideEffectCompiler.cpp +559 -211
@@ 24,13 24,109 @@ 
 #include <sstream>
 #endif
 
+#define NI64(op,a,b) Nodes::Native::MakeInt64(#op, Native::op, a, b)
+#define NI32(op,a,b) Nodes::Native::MakeInt32(#op, Native::op, a, b)
+#define FI32(op,a,b) Nodes::Native::MakeFloat(#op, Native::op, a, b)
+
+#define INSTRUMENT_FUNCTIONS 0
+
+#if INSTRUMENT_FUNCTIONS
+template <typename T> void Consume(std::ostream& to, const void*& data) {
+	to << *(T*)data;
+	data = (const char*)data + sizeof(T);
+}
+
+const char* ToStream(std::ostream& os, const char* typeInfo, const void*& dataBlob) {
+	if (dataBlob) {
+		for (;;) {
+			switch (char c = *typeInfo++) {
+			case '\0': return typeInfo;
+			case '%':
+				c = *typeInfo++;
+				switch (c) {
+				case 'f': Consume<float>(os, dataBlob); break;
+				case 'd': Consume<double>(os, dataBlob);  break;
+				case 'i': Consume<std::int32_t>(os, dataBlob);  break;
+				case 'q': Consume<std::int64_t>(os, dataBlob);  break;
+				case '[': {
+					char *loopPoint;
+					for (auto loopCount = strtoull(typeInfo, &loopPoint, 10); loopCount; --loopCount)
+						typeInfo = ToStream(os, loopPoint + 1, dataBlob);
+					break;
+				}
+				case ']': return typeInfo;
+				case '%': os.put('%'); break;
+				default:
+					assert(0 && "Bad format string");
+				}
+				break;
+			default:
+				os.put(c);
+				break;
+			}
+		}
+	} else {
+		for (;;) {
+			switch (char c = *typeInfo++) {
+			case '\0': return typeInfo;
+			case '%':
+				c = *typeInfo++;
+				switch (c) {
+				case 'f': os << "\"Float\""; break;
+				case 'd': os << "\"Double\"";  break;
+				case 'i': os << "\"Int32\"";  break;
+				case 'q': os << "\"Int64\""; break;
+				case '[': {
+					char *loopPoint;
+					for (auto loopCount = strtoull(typeInfo, &loopPoint, 10); loopCount; --loopCount)
+						typeInfo = ToStream(os, loopPoint + 1, dataBlob);
+				}
+				case ']': break;
+				case '%': os.put('%'); break;
+
+				default:
+					assert(0 && "Bad format string");
+				}
+				break;
+			default:
+				os.put(c);
+				break;
+			}
+		}
+	}
+	return typeInfo;
+}
+KRONOS_ABI_EXPORT int32_t kvm_label(const char *l) {
+	std::clog << "\n\n[" << l << "]\n";
+	return 0;
+}
+KRONOS_ABI_EXPORT int32_t kvm_instrument(int32_t chain, const char *l, const void* data, const char* fmt) {
+	std::clog << l << " : "; ToStream(std::clog, fmt, data);
+	std::clog << "\n";
+	return chain;
+}
+#endif
+
 namespace K3
 {
+	static CTRef Canonicalize(CTRef data, CRRef r, const Type& e, bool referenced, bool mustCopy, Backends::SideEffectTransform& sfx);
+
+	static bool CheckForArgInputs(CTRef dst) {
+		return Qxx::FromGraph(dst).OfType<SubroutineArgument>().Where(
+			[](auto sa) {
+			if (sa->IsSelf()) return false;
+			if (sa->IsLocalState()) return false;
+			if (sa->IsState()) return false;
+			if (sa->IsOutput()) return false;
+			return true;
+		}).Any();
+	}
+
 #pragma region side effect transform infrastructure
 	namespace Backends
 	{
 		static bool IsPair(CTRef graph) {
-			const Monad *m;
+			const Deps *m;
 			if (graph->Cast(m)) return IsPair(m->GetUp(0));
 			const DataSource *ds;
 			if (graph->Cast(ds)) return ds->HasPairLayout();

          
@@ 38,7 134,7 @@ namespace K3
 		}
 
 		static bool IsReference(CTRef graph) {
-			const Monad *m;
+			const Deps *m;
 			if (graph->Cast(m)) return IsReference(m->GetUp(0));
 			const DataSource *ds;
 			if (graph->Cast(ds)) return ds->IsReference();

          
@@ 46,24 142,24 @@ namespace K3
 		}
 
 		static CTRef DereferenceOnce(CTRef graph) {
-			//const Monad *m;
-			//if (graph->Cast(m)) return Monad::Transfer(DereferenceOnce(m->GetUp(0)),m);
+			//const Deps *m;
+			//if (graph->Cast(m)) return Deps::Transfer(DereferenceOnce(m->GetUp(0)),m);
 			const DataSource *ds;
 			if (graph->Cast(ds) && ds->IsReference()) return ds->Dereference();
 			else return graph;
 		}
 
 		static CTRef DereferenceAll(CTRef graph) {
-			//const Monad *m;
-			//if (graph->Cast(m)) return Monad::Transfer(DereferenceAll(m->GetUp(0)),m);
+			//const Deps *m;
+			//if (graph->Cast(m)) return Deps::Transfer(DereferenceAll(m->GetUp(0)),m);
 			const DataSource *ds;
 			if (graph->Cast(ds) && ds->IsReference()) return DereferenceAll(ds->Dereference());
 			else return graph;
 		}
 
 		static CTRef SplitFirst(CTRef graph) {
-			const Monad *m;
-			if (graph->Cast(m)) return Monad::Transfer(SplitFirst(m->GetUp(0)), m);
+			const Deps *m;
+			if (graph->Cast(m)) return Deps::Transfer(SplitFirst(m->GetUp(0)), m);
 
 			const DataSource *ds;
 			if (graph->Cast(ds)) return ds->First();

          
@@ 71,8 167,8 @@ namespace K3
 		}
 
 		static CTRef SplitRest(CTRef graph) {
-			const Monad *m;
-			if (graph->Cast(m)) return Monad::Transfer(SplitRest(m->GetUp(0)), m);
+			const Deps *m;
+			if (graph->Cast(m)) return Deps::Transfer(SplitRest(m->GetUp(0)), m);
 
 			const DataSource *ds;
 			if (graph->Cast(ds)) return ds->Rest();

          
@@ 113,8 209,8 @@ namespace K3
 		}
 
 		static CTRef GetAccessor(CTRef graph) {
-			const Monad *m;
-			if (graph->Cast(m)) return Monad::Transfer(GetAccessor(m->GetUp(0)), m);
+			const Deps *m;
+			if (graph->Cast(m)) return Deps::Transfer(GetAccessor(m->GetUp(0)), m);
 
 			const DataSource *ds;
 			if (graph->Cast(ds)) return ds->GetAccessor();

          
@@ 123,7 219,7 @@ namespace K3
 
 		static CTRef ComputeSize(CTRef node) {
 			const Pair* p;
-			if (node->Cast(p)) return Native::MakeInt64("uadd", Native::Add, ComputeSize(p->GetUp(0)), ComputeSize(p->GetUp(1)));
+			if (node->Cast(p)) return NI64(Add, ComputeSize(p->GetUp(0)), ComputeSize(p->GetUp(1)));
 
 			const IFixedResultType *fr;
 			if (node->Cast(fr)) return Native::Constant::New(int64_t(fr->FixedResult().GetSize()));

          
@@ 134,11 230,15 @@ namespace K3
 			const DataSource *ds;
 			if (node->Cast(ds)) return ds->SizeOf();
 
-			const Monad *m;
+			const Deps *m;
 			if (node->Cast(m)) return ComputeSize(m->GetUp(0));
 
 			const VariantTuple *vt;
-			if (node->Cast(vt)) return Native::MakeInt64("_mul",Native::Mul, vt->GetUp(1), ComputeSize(vt->GetUp(0)));
+			if (node->Cast(vt)) {
+				return NI64(Add, 
+							NI64(Mul, vt->GetUp(1), ComputeSize(vt->GetUp(0))),
+							ComputeSize(vt->GetUp(2)));
+			}
 
 			const Boundary *b;
 			if (node->Cast(b)) return ComputeSize(b->GetUp(0));

          
@@ 160,7 260,7 @@ namespace K3
 			if (auto ds = out->Cast<DataSource>()) {
 				return ValidateSfxOutput(ds->GetAccessor());
 			}
-/*			if (auto proc = out->Cast<Monad>()) {
+/*			if (auto proc = out->Cast<Deps>()) {
 				return ValidateSfxOutput(proc->GetUp(0));
 			}*/
 			for (auto &u : out->Upstream()) {

          
@@ 176,17 276,71 @@ namespace K3
 			return result;
 		}
 
-		void SideEffectTransform::CompileSubroutineAsync(Subroutine* subr, CTRef sourceBody, CTRef args, CTRef results, bool wait) {
+	#if INSTRUMENT_FUNCTIONS
+		static CTRef InstrumentLabel(CTRef chain, const char *l) {
+			auto diags = Native::ForeignFunction::New("int32", "kvm_label");
+			diags->AddParameter("int32_t", chain, Type::Int32);
+			diags->AddParameter("const char*", CStringLiteral::New(Type(l)), Type(l));
+			return diags;
+		}
+
+		static CTRef InstrumentData(SideEffectTransform& sfx, const char* label, const Type& ty, CTRef data, CTRef chain) {
+			if (IsPair(data)) {
+				return InstrumentData(sfx, label, ty.Rest(), SplitRest(data),
+									  InstrumentData(sfx, label, ty.First(), SplitFirst(data), chain));
+			} else {
+				auto diags = Native::ForeignFunction::New("int32", "kvm_instrument");
+				diags->AddParameter("int32", chain, Type::Int32);
+				diags->AddParameter("const char*", CStringLiteral::New(Type(label)), Type(label));
+
+				if (ty.GetSize()) {
+					data = Canonicalize(data, data->GetReactivity(), ty, true, false, sfx);
+				} else {
+					data = CStringLiteral::New(Type("()"));
+				}
+
+				diags->AddParameter("const void*", GetAccessor(data), ty);
+
+				std::stringstream tyS;
+				ty.OutputFormatString(tyS);
+
+				tyS << " (";
+
+				std::stringstream dataBundle;
+				dataBundle << "(" << *data << ")";
+
+				for (auto c : dataBundle.str()) {
+					switch (c) {
+					case '\n': break;
+					case '%': tyS << "%%"; break;
+					default: tyS.put(c); break;
+					}
+				}
+
+				tyS << ")";
+
+
+				Type tyStr(tyS.str().c_str());
+				diags->AddParameter("const char*", CStringLiteral::New(tyStr), tyStr);
+				return diags;
+			}
+		}
+	#endif
+
+		void SideEffectTransform::CompileSubroutineAsync(const char* label, const Type& arg, const Type& res, Subroutine* subr, CTRef sourceBody, CTRef args, CTRef results, bool wait) {
+			assert(!CheckForArgInputs(results));
+
 			TLS::WithNewStack([&]() mutable {
 #ifndef NDEBUG
 				if (TLS::GetCurrentInstance()->ShouldTrace("Compile", subr->GetLabel())) {
 					std::clog << "Compile: [" << subr->GetLabel() << "]\n" << *sourceBody << "\n\nArgs: " << *args << "\nResults: " << *results ;
 				}
 #endif
-				subr->SetBody(Compile(symbols, sourceBody, args, results));
+				auto body = Compile(symbols, sourceBody, args, results, label, arg, res);
+				subr->SetBody(body);
 				return 0;
 			});
-            Monad *m;
+            Deps *m;
             SubroutineMeta *sm;
             if (subr->GetBody()->Cast(m) && m->GetUp(m->GetNumCons()-1)->Cast(sm)) {
                 if (sm->HasLocalState) AllocatesState();

          
@@ 257,9 411,9 @@ namespace K3
 					auto h1 = (*this)(node->GetUp(0)) + (int)o;
 					auto h2 = (*this)(node->GetUp(1));
 					return h1 | h2;
-				}
+				} 
 				
-				if (IS_A(Monad, AtIndex, Reference, BoundaryBuffer)) {
+				if (IS_A(Deps, AtIndex, Reference, BoundaryBuffer, BitCast)) {
 					// these nodes may pass undereferenced pointers from up to downstream
 					if (node->GetNumCons() == 0) return false;
 					for (unsigned i(1); i < node->GetNumCons(); ++i) (*this)(node->GetUp(i));

          
@@ 312,13 466,13 @@ namespace K3
 		};
 
 		CTRef SideEffectTransform::Process(CTRef body, const Reactive::Node *rootReactivity) {
-			CTRef processed = Monad::New(CopyData(results, (*this)(body), rootReactivity, true, false, false));
+			CTRef processed = Deps::New(CopyData(results, (*this)(body), rootReactivity, true, false, false));
 
 			struct HazardousEffect {
 				CTRef ReadPointer;
 				CTRef Pure;
 				CTRef Effect;
-				Monad *Guard;
+				Deps *Guard;
 				std::int64_t size;
 			};
 			std::vector<HazardousEffect> protectForHazards;

          
@@ 335,7 489,7 @@ namespace K3
 
 				if (fx.WriteValue == nullptr) {
 					// this side effect releases the allocation
-					hfx.Guard = Monad::New();
+					hfx.Guard = Deps::New();
 					hfx.Guard->Connect(fx.WritePointer);
 					hfx.Effect = ReleaseBuffer::New(hfx.Guard, fx.ReadPointer->GetUp(0));
 					protectForHazards.emplace_back(hfx);

          
@@ 349,7 503,7 @@ namespace K3
 							HazardousEffect write = hfx;
 							write.Effect = cpy;
 							write.Pure = cpy->GetUp(1);
-							write.Guard = Monad::New();
+							write.Guard = Deps::New();
 							write.Guard->Connect(cpy->GetUp(0));
 							cpy->Reconnect(0, write.Guard);
 							protectForHazards.emplace_back(write);

          
@@ 366,7 520,7 @@ namespace K3
 				SetStatePointer(recursiveBranch);
 			}
 
-			auto complete = Monad::New();
+			auto complete = Deps::New();
 			complete->Connect(GetStatePointer());
 			complete->Connect(processed);
 

          
@@ 418,7 572,7 @@ namespace K3
 		}
 
 		CTRef SideEffectTransform::GetDataLayout(CTRef graph) {
-			Monad *m;
+			Deps *m;
 			if (graph->Cast(m)) return GetDataLayout(m->GetUp(0));
 
 			const DataSource *ds;

          
@@ 430,8 584,8 @@ namespace K3
 		}
 
 		CTRef SideEffectTransform::GetDataAccessor(CTRef graph) {
-			Monad *m;
-			if (graph->Cast(m)) return Monad::Transfer(GetDataAccessor(m->GetUp(0)), m);
+			Deps *m;
+			if (graph->Cast(m)) return Deps::Transfer(GetDataAccessor(m->GetUp(0)), m);
 
 			const DataSource *ds;
 			if (graph->Cast(ds)) return ds->GetAccessor();

          
@@ 439,8 593,8 @@ namespace K3
 		}
 
 		CTRef SideEffectTransform::GetDereferencedAccessor(CTRef graph) {
-			Monad *m;
-			if (graph->Cast(m)) return Monad::Transfer(GetDereferencedAccessor(m->GetUp(0)), m);
+			Deps *m;
+			if (graph->Cast(m)) return Deps::Transfer(GetDereferencedAccessor(m->GetUp(0)), m);
 
 			const DataSource *ds;
 			if (graph->Cast(ds)) {

          
@@ 449,12 603,12 @@ namespace K3
 			} else return graph;
 		}
 
-		static CTRef TransferDependencies(CTRef src, bool deref, Monad*& deps) {
+		static CTRef TransferDependencies(CTRef src, bool deref, Deps*& deps) {
 			if (deref) src = DereferenceAll(src);
 
-			Monad *m;
+			Deps *m;
 			if (src->Cast(m)) {
-				if (deps == nullptr) deps = Monad::New( );
+				if (deps == nullptr) deps = Deps::New( );
 				for (unsigned i(0); i < m->GetNumCons(); ++i)
 					deps->Connect(m->GetUp(i));
 

          
@@ 495,7 649,9 @@ namespace K3
 		}
 
 		CTRef SideEffectTransform::CopyData(CTRef dst, CTRef src, CRRef reactivity, bool byVal, bool mutatesState, bool doesInit) {
-			Monad *deps(nullptr);
+			Deps *deps(nullptr);
+
+			assert(!CheckForArgInputs(dst));
 
 			dst = TransferDependencies(dst, byVal, deps);
 			src = TransferDependencies(src, byVal, deps);

          
@@ 505,7 661,7 @@ namespace K3
 			bool dstIsPair = IsPair(dst), srcIsPair = IsPair(src);
 
 			if (dstIsPair || srcIsPair || !IsFused(reactivity)) {
-				return Monad::New(CopyData(SplitFirst(dst), SplitFirst(src), reactivity ? reactivity->First() : 0, byVal, mutatesState,doesInit),
+				return Deps::New(CopyData(SplitFirst(dst), SplitFirst(src), reactivity ? reactivity->First() : 0, byVal, mutatesState,doesInit),
 					CopyData(SplitRest(dst), SplitRest(src), reactivity ? reactivity->Rest() : 0, byVal, mutatesState,doesInit));
 			}
 

          
@@ 522,7 678,7 @@ namespace K3
 					finalSz = dst_ds->SizeOf();
 					finalMode = Copy::MemCpy;
 				} else if (Typed::IsNil(src)) {
-					return deps ? Monad::Transfer(Typed::Nil(), deps) : src;
+					return deps ? Deps::Transfer(Typed::Nil(), deps) : src;
 				} else {
 					ResultTypeWithNoArgument tmp(src);
 

          
@@ 532,24 688,24 @@ namespace K3
 					finalMode = Copy::Store;
 				}
 
-				/* flatten out any monads inside data sources */
+				/* flatten out any Depss inside data sources */
 				finalSrc = TransferDependencies(finalSrc, false, deps);
 				finalDst = TransferDependencies(finalDst, false, deps);
 
 				/* elided copy or null destination */
 				if (*finalDst == *finalSrc || Typed::IsNil(dst_ds->GetAccessor())) {
-					if (deps) return Monad::Transfer(Typed::Nil(), deps);
+					if (deps) return Deps::Transfer(Typed::Nil(), deps);
 					else return Typed::Nil();
 				}
 
 
 				CTRef result(0);
-				if (deps) finalDst = Monad::Transfer(finalDst, deps);
+				if (deps) finalDst = Deps::Transfer(finalDst, deps);
 				assert(reactivity == nullptr || reactivity->IsFused());
 				result = Copy::New(finalDst, finalSrc, finalSz, finalMode, reactivity, 1, mutatesState, doesInit);
 				return result;
 			} else if (Typed::IsNil(dst)) {
-				if (deps) return Monad::Transfer(dst, deps);
+				if (deps) return Deps::Transfer(dst, deps);
 				else return dst;
 			} else {
 				INTERNAL_ERROR("Bad copy operation");

          
@@ 558,7 714,7 @@ namespace K3
 
 		bool GraphvizReduceProcEdgeWeight(std::ostream& dot, CTRef d, CTRef u) {
 			// reduce layout weight of procedural data-less flows
-			if (IsOfExactType<Monad>(d) || IsOfExactType<Monad>(u)) {
+			if (IsOfExactType<Deps>(d) || IsOfExactType<Deps>(u)) {
 				if (d->GetNumCons() && d->GetUp(0) == u) return false;
 				dot << "n" << u << " -> n" << d << " [weight=0.2, style=dashed, color=gray];\n";
 				return true;

          
@@ 587,11 743,11 @@ namespace K3
 
 			SubroutineArgument *arg;
 			if (n->Cast(arg)) {
-				dot << "n" << n << " [label=\"arg" << arg->GetID() << ":" << arg->FixedResult() << "\"];\n";
+				dot << "n" << n << " [label=\"arg" << arg->GetID() << "\"];\n";
 				return true;
 			}
 
-			if (IsOfExactType<Monad>(n)) {
+			if (IsOfExactType<Deps>(n)) {
 				dot << "n" << n << " [label=\"Depends\"];\n";
 				return true;
 			}

          
@@ 599,7 755,7 @@ namespace K3
 			return false;
 		}
 
-		Graph<Typed> SideEffectTransform::Compile(IInstanceSymbolTable& symbols, const CTRef pureBody, const CTRef arguments, const CTRef results) {
+		Graph<Typed> SideEffectTransform::Compile(IInstanceSymbolTable& symbols, const CTRef pureBody, const CTRef arguments, const CTRef results, const char *l, const Type& argTy, const Type& resTy) {
 			auto result = Graph<Typed>{};
 			if (symbols.GetMemoized(std::make_tuple(pureBody, arguments, results), result)) {
 				return result;

          
@@ 670,14 826,30 @@ namespace K3
 				symbols.SetMemoized(std::make_tuple(pureBody, arguments, results), finalBody);
 
 				//ExportGraphviz(std::clog, "sfx", finalBody, GraphvizReduceOffsets, GraphvizReduceProcEdgeWeight);
+			#if INSTRUMENT_FUNCTIONS
+				if (l) {
+					Deps* d = nullptr;
+					finalBody->Cast(d);
+					auto meta = d->GetUp(d->GetNumCons() - 1);
+					d->Reconnect(d->GetNumCons() - 1, 
+								 InstrumentData(sfx, "->", resTy, results,
+									InstrumentData(sfx, "<-", argTy, arguments,
+									   InstrumentLabel(
+										   Deps::New(Native::Constant::New((int32_t)0), d->GetUp(0)), l))));
+					d->Connect(meta);
+				}
+			#endif
+
 				return finalBody;
 			}
 		}
 
 		Graph<Typed> SideEffectTransform::Compile(IInstanceSymbolTable& symbols, CTRef body, const Type& arg, const Type& res) {
-			return Compile(symbols, body,
+			auto cmp = Compile(symbols, body,
 				DataSource::New(SubroutineArgument::In(1,4), Reference::New(Native::Constant::New(arg, 0))),
-				DataSource::New(SubroutineArgument::Out(2,4), Reference::New(Native::Constant::New(res, 0))));
+				DataSource::New(SubroutineArgument::Out(2,4), Reference::New(Native::Constant::New(res, 0))), "root", arg, res);
+
+			return cmp;
 		}
 
 		K3::TypeDescriptor pointerTag("Ptr");

          
@@ 703,7 875,7 @@ namespace K3
 			if (node->Cast(ref)) return NeverHasData(ref->GetUp(0));
 			const Dereference *deref;
 			if (node->Cast(deref)) return NeverHasData(deref->GetUp(0));
-			const Monad* m;
+			const Deps* m;
 			if (node->Cast(m)) return NeverHasData(m->GetUp(0));
 			const Offset* ofs;
 			if (node->Cast(ofs)) return NeverHasData(m->GetUp(0));

          
@@ 736,7 908,7 @@ namespace K3
 			const DataSource* ds;
 			if (node->Cast(ds)) return ReduceType(ds->GetDataLayout());
 
-			const Monad *m;
+			const Deps *m;
 			if (node->Cast(m)) return ReduceType(m->GetUp(0));
 
 			const BoundaryBuffer* b;

          
@@ 785,6 957,7 @@ namespace K3
 			default:
 				strm << (isOutput ? "%out" : "%in") << ID - 1;
 			}
+			strm << "<" << type << ">";
 		}
 
 		unsigned SubroutineArgument::ComputeLocalHash() const {

          
@@ 842,9 1015,9 @@ namespace K3
 		}
 
 		CTRef Reference::New(CTRef upstream) {
-			/* maintain Monadic dependencies */
-			Monad *m;
-			if (upstream->Cast(m)) return Monad::Transfer(New(upstream->GetUp(0)), m);
+			/* maintain Depsic dependencies */
+			Deps *m;
+			if (upstream->Cast(m)) return Deps::Transfer(New(upstream->GetUp(0)), m);
 
 			/* simplify */
 			Dereference *dr;

          
@@ 853,15 1026,15 @@ namespace K3
 		}
 
 		CTRef Offset::New(CTRef buffer, CTRef offset) {
-			/* maintain Monadic dependencies */
+			/* maintain Depsic dependencies */
 			Native::Constant *constOffset;
 			if (offset->Cast(constOffset)) {
 				if (constOffset->FixedResult() == Type::Int64 && *(int64_t*)constOffset->GetPointer() == 0) {
 					return buffer;
 				}
 			}
-			Monad *m;
-			if (buffer->Cast(m)) return Monad::Transfer(New(m->GetUp(0), offset), m);
+			Deps *m;
+			if (buffer->Cast(m)) return Deps::Transfer(New(m->GetUp(0), offset), m);
 			else return new Offset(buffer, offset);
 		}
 

          
@@ 925,7 1098,7 @@ namespace K3
 			auto a(GetAccessor());
 			do {
 				if (IsOfExactType<Nodes::Dereference>(a)) return true;
-			} while (IsOfExactType<Monad>(a) && (a = a->GetUp(0)));
+			} while (IsOfExactType<Deps>(a) && (a = a->GetUp(0)));
 			return false;
 		}
 

          
@@ 991,7 1164,7 @@ namespace K3
 			auto buf(Buffer::New(sfx, match->SizeOf(), Buffer::Stack, 16));
 			auto buf_ds(DataSource::New(buf, match->Reference()->GetDataLayout()));
 			return DataSource::New(
-				Monad::New(buf_ds->Dereference()->GetAccessor(), sfx.CopyData(buf_ds, this, r, true, false, false)),
+				Deps::New(buf_ds->Dereference()->GetAccessor(), sfx.CopyData(buf_ds, this, r, true, false, false)),
 				match->GetDataLayout());
 		}
 

          
@@ 1003,7 1176,7 @@ namespace K3
 		}
 
 		CTRef VariantTuple::GraphRest() const {
-			return VariantTuple::New(GetUp(0), Native::MakeInt64("_sub", Native::Sub, GetUp(1), Native::Constant::New(int64_t(1))), GetUp(2), GetRecurrenceDelta());
+			return VariantTuple::New(GetUp(0), NI64(Sub, GetUp(1), Native::Constant::New(int64_t(1))), GetUp(2), GetRecurrenceDelta());
 		}
 
 		Buffer* Buffer::ConstructShallowCopy() const {

          
@@ 1052,30 1225,30 @@ namespace K3
 			strm << "Buffer" << GUID;
 		}
 
-		CTRef Monad::GraphFirst() const {
+		CTRef Deps::GraphFirst() const {
 			return Transfer(First::New(GetUp(0)), this);
 			//New(First::New(GetUp(0)),this);
 		}
 
-		CTRef Monad::GraphRest() const {
+		CTRef Deps::GraphRest() const {
 			return Transfer(Rest::New(GetUp(0)), this);
 			//			return New(Rest::New(GetUp(0)),this);
 		}
 
-		CTRef Monad::IdentityTransform(GraphTransform<const Typed, CTRef>& state) const {
+		CTRef Deps::IdentityTransform(GraphTransform<const Typed, CTRef>& state) const {
 			if (GetNumCons() == 1) return state(GetUp(0));
 			else return TypedPolyadic::IdentityTransform(state);
 		}
 
-		CTRef Monad::Transfer(CTRef upstream, const Monad* deps) {
+		CTRef Deps::Transfer(CTRef upstream, const Deps* deps) {
 			DataSource *ds;
 			if (upstream->Cast(ds)) {
 				return DataSource::New(Transfer(ds->GetAccessor(), deps), ds->GetDataLayout());
 			}
-			Monad *m(New());
+			Deps *m(New());
 			m->Connect(upstream);
 			if (deps->isDataProtector) {
-				/* must maintain this monad intact because its identity (via pointer) matters */
+				/* must maintain this Deps intact because its identity (via pointer) matters */
 				m->Connect(deps);
 			} else {
 				for (unsigned i(1); i < deps->GetNumCons(); ++i) m->Connect(deps->GetUp(i));

          
@@ 1083,14 1256,14 @@ namespace K3
 			return m;
 		}
 
-		void Monad::Connect(CTRef dependency) {
+		void Deps::Connect(CTRef dependency) {
 			if (GetNumCons() == 0) SetReactivity(dependency->GetReactivity());
 			DataSource *ds;
-			Monad *m;
+			Deps *m;
 			Pair *p;
 			Invariant::Constant *ic;
 			if (dependency->Cast(m) && m->isDataProtector == false) {
-				/* flatten chained Monads that are not data protectors */
+				/* flatten chained Depss that are not data protectors */
 				for (auto up : m->Upstream()) {
 					if (Typed::Nil() != (up)) Connect(up);
 				}

          
@@ 1107,9 1280,12 @@ namespace K3
 			}
 		}
 
-		CTRef SubroutineArgument::In(size_t ID, CTRef graph, const char *l) {
-			Monad *m;
-			if (graph->Cast(m)) return In(ID, m->GetUp(0), l);
+		CTRef SubroutineArgument::New(bool isInput, size_t ID, CTRef graph, const char *l) {
+			if (l == nullptr) {
+				l = isInput ? "in" : "out";
+			}
+			Deps *m;
+			if (graph->Cast(m)) return New(isInput, ID, m->GetUp(0), l);
 
 			const DataSource *ds;
 			if (graph->Cast(ds)) {

          
@@ 1118,11 1294,20 @@ namespace K3
 					int align = 0;
 					IAlignmentTrackerNode *at;
 					if (ds->GetAccessor()->Cast(at)) align = at->GetAlignment();
-					return In(ID, align, l);
+					if (isInput) {
+						return In(ID, align, l);
+					} else {
+						return Out(ID, align, l);
+					}
 				}
 			}
 			ResultTypeWithNoArgument rtnoa(graph);
-			return In(ID, graph->Result(rtnoa), l);
+			auto t = graph->Result(rtnoa);
+			if (isInput) {
+				return In(ID, t, l);
+			} else {
+				return Out(ID, t, l);
+			}
 		}
 #pragma endregion 
 

          
@@ 1141,10 1326,10 @@ namespace K3
 			return IdentityTransform(sfx);
 		}
 
-		CTRef Monad::SideEffects(SideEffectTransform& sfx) const {
-			Monad *side(Monad::New());
+		CTRef Deps::SideEffects(SideEffectTransform& sfx) const {
+			Deps *side(Deps::New());
 			for (unsigned i(1); i < GetNumCons(); ++i) side->Connect(GetAccessor(sfx(GetUp(i))));
-			if (GetNumCons()) return Monad::New(sfx(GetUp(0)), side);
+			if (GetNumCons()) return Deps::New(sfx(GetUp(0)), side);
 			else return side;
 		}
 

          
@@ 1164,7 1349,7 @@ namespace K3
 			auto ptr = GetSlot::New(sfx.GetSymbolTable().GetIndex(uid));
 				
 				/*Offset::New(SubroutineArgument::Self(),
-								   Native::MakeInt64("umul", Native::Mul,
+								   NI64(Mul,
 													 Native::Constant::New(int64_t(sfx.GetSymbolTable().GetIndex(uid))),
 													 SizeOfPointer::New()));*/
 

          
@@ 1178,7 1363,7 @@ namespace K3
 			const DataSource* ptr_ds = DataSource::New(ptr, Reference::New(ReduceType(GetUp(0))));
 
 			const DataSource* new_ds;
-			const Monad* new_m;
+			const Deps* new_m;
 			if (new_val->Cast(new_m) && new_m->GetUp(0)->Cast(new_ds)) {
 				auto tmp1(ptr_ds), tmp2(new_ds);
 				while (tmp1->IsReference()) tmp1 = tmp1->Dereference();

          
@@ 1187,32 1372,40 @@ namespace K3
 					//return sfx.CopyData(ptr_ds->Dereference(),tmp2->Reference(),0,false);
 					return Copy::New(ptr_ds->GetAccessor(),
 									 tmp2->Reference()->Reference()->GetAccessor(),
-									 Monad::Transfer(SizeOfPointer::New(), new_m), Copy::Store, new_val->GetReactivity(), 1, true, false);
+									 Deps::Transfer(SizeOfPointer::New(), new_m), Copy::Store, new_val->GetReactivity(), 1, true, false);
 				}
 			}
 
 			auto buf(Buffer::New(sfx, ptr_ds->Dereference()->Dereference()->SizeOf(), Buffer::Stack, 16));
 			const DataSource* buf_ds = DataSource::New(buf, ptr_ds->Dereference()->GetDataLayout());
-			return Monad::New(Copy::New(ptr_ds->GetAccessor(), Reference::New(buf), SizeOfPointer::New(), Copy::Store, 0, 1, true, false),
+			return Deps::New(Copy::New(ptr_ds->GetAccessor(), Reference::New(buf), SizeOfPointer::New(), Copy::Store, 0, 1, true, false),
 							  sfx.CopyData(buf_ds, new_val, 0, true, true, false));
 		}
 
 		CTRef GetGlobalVariable::SideEffects(SideEffectTransform& sfx) const {
-			CTRef initializer = nullptr;
+			CTRef initializer = nullptr;;
 			auto layout(Reference::New(Native::Constant::New(t, 0)));
 
+			auto slotIndex = sfx.GetSymbolTable().GetIndex(uid);
+
 			if (GetNumCons()) {
+				initializer = sfx(GetUp(0));
+			} else if (IsConfigurationSlot()) {
+				initializer = DataSource::New(Configuration::New(t, slotIndex), layout);
+			}
+
+			if (initializer) {
 				/* this variable has an initializer */
 				auto buf = Buffer::New(sfx, Native::Constant::New(int64_t(t.GetSize())), Buffer::Module, 16);
-				initializer = Monad::New(buf, sfx.CopyData(DataSource::New(buf, layout), sfx(GetUp(0)), sfx.GetSymbolTable().GetInitializerReactivity(), true, true, true));
+				initializer = Deps::New(buf, sfx.CopyData(DataSource::New(buf, layout), initializer, 
+									    sfx.GetSymbolTable().GetInitializerReactivity(), true, true, true));
+			} 
+
+			if (key.IsNil() == false) {
+				sfx.GetSymbolTable().RegisterExternalVariable(key, t, uid, k, vectorRate, clock);
 			}
 
-			Typed* ptr = GetSlot::New(sfx.GetSymbolTable().GetIndex(uid), initializer);
-
-			if (key.IsNil() == false)
-				sfx.GetSymbolTable().RegisterExternalVariable(key, t, uid, k, vectorRate, clock);
-
-			return DataSource::New(ptr, layout);
+			return DataSource::New(GetSlot::New(slotIndex, initializer), layout);
 		}
 
 		CTRef ExternalAsset::SideEffects(SideEffectTransform& sfx) const {

          
@@ 1271,11 1464,11 @@ namespace K3
 				while (up_ds->IsReference()) up_ds = up_ds->Dereference();
 				return DataSource::New(
 					BoundaryBuffer::New(up_ds->Reference()->GetAccessor(),
-										Monad::New(bufferPtr, sfx.CopyData(buffer, upstream, up, true, true, true)), up),
+										Deps::New(bufferPtr, sfx.CopyData(buffer, upstream, up, true, true, true)), up),
 					buffer->GetDataLayout());
 			} else {
 				return
-					BoundaryBuffer::New(sfx.GetDereferencedAccessor(upstream), Monad::New(sfx.GetDereferencedAccessor(buffer),
+					BoundaryBuffer::New(sfx.GetDereferencedAccessor(upstream), Deps::New(sfx.GetDereferencedAccessor(buffer),
 																						  sfx.CopyData(buffer, upstream, up, true, true, true)), up);
 			}
 		}

          
@@ 1349,7 1542,7 @@ namespace K3
 
 			auto bufferPtr(sfx.GetStatePointer());
 			auto buffer(DataSource::New(bufferPtr, Reference::New(Native::Constant::New(FixedResult(),nullptr))));
-			CTRef output = Monad::New(buffer);
+			CTRef output = Deps::New(buffer);
 			sfx.SetStatePointer(Offset::New(bufferPtr, buffer->Dereference()->SizeOf()));
 
 			for (unsigned i(0);i < GetNumCons();++i) {

          
@@ 1359,7 1552,7 @@ namespace K3
 				if (upstream->Cast(up_ds) && up_ds->IsReference()) {
 					while (up_ds->IsReference()) up_ds = up_ds->Dereference();
 				}
-				output = Monad::New(output, sfx.CopyData(buffer, upstream, up, true, true, true));
+				output = Deps::New(output, sfx.CopyData(buffer, upstream, up, true, true, true));
 			}
 			return output;
 		}

          
@@ 1372,25 1565,9 @@ namespace K3
 			} else return false;
 		}
 
-		static CTRef Canonicalize(CTRef data, CRRef r, const Type& e, bool referenced, bool mustCopy, SideEffectTransform& sfx) {
-			if (mustCopy == false) {
-				const DataSource* ds;
-				data = DereferenceAll(data);
-				if (data->Cast(ds) && ds->HasPairLayout() == false && IsFused(r)) {
-					if (referenced) {					
-						if (ds->CanTakeReference()) return ds->Reference();
-					} else return ds;
-				}
-			}
-			auto buf(Buffer::New(sfx,e.GetSize(), Buffer::Stack));
-			auto buf_ds(DataSource::New(buf, Reference::New(Native::Constant::New(e, 0))));
-			auto val = Monad::New(buf_ds, sfx.CopyData(buf_ds, data, r, true, false, false));
-			if (!referenced) return DereferenceOnce(val);
-			else return val;
-		}
-
 		namespace Native {
 			CTRef ForeignFunction::SideEffects(SideEffectTransform& sfx) const {
+
 				ForeignFunction *ff = ConstructShallowCopy();
 				if (Symbol.back() == '!') sfx.MutatesGVars();
 				CTRef returnValue = DataSource::New(Typed::Nil(), Native::Constant::New(Type(false), nullptr));

          
@@ 1423,7 1600,7 @@ namespace K3
 				}
 				return Pair::New(
 					DataSource::New(ff, Native::Constant::New(ff->ReturnValue, nullptr)),
-					Monad::New(returnValue, ff));
+					Deps::New(returnValue, ff));
 			}
 
 			CTRef SelfID::SideEffects(SideEffectTransform& sfx) const {

          
@@ 1474,38 1651,115 @@ namespace K3
 			return DataSource::New(tree, layout);
 		}
 
-		CTRef Memory::SideEffects(SideEffectTransform& sfx) const {
-			CTRef bufferPtr = sfx.GetLocalStatePointer();
-			CTRef tableSetter = sfx(GetUp(0));
-			sfx.SetLocalStatePointer(Offset::New(bufferPtr, Native::Constant::New(int64_t(sz * elTy.GetSize()))));
-			CTRef table = DataSource::New(
-				bufferPtr,
-				Reference::New(Native::Constant::New(Type::Chain(elTy, sz, Type::Nil),nullptr)));
-
-			auto react = GetReactivity();
-			if (Qxx::FromGraph(react).OfType<Reactive::DriverNode>().Any() == false) {
-				react = sfx.GetSymbolTable().GetInitializerReactivity();
+		static void GetSliceBounds(Backends::SideEffectTransform& sfx, int staticLen, const Type& elementType, 
+								   CRRef rx, CTRef vec, CTRef& ptr, CTRef &offset, CTRef &limit) {
+			if (staticLen < 0) {
+				ptr = BitCast::New(Type::ArrayView(elementType),
+								   GetAccessor(DereferenceAll(SplitFirst(vec))));
+				auto ofsl = SplitRest(vec);
+				offset = GetAccessor(DereferenceAll(SplitFirst(ofsl)));
+				limit = GetAccessor(DereferenceAll(SplitRest(ofsl)));
+			} else {
+				auto bufType = Type::List(elementType, staticLen);
+				ptr = GetAccessor(Canonicalize(vec, rx, bufType, true, false, sfx));
+				limit = Native::Constant::New((int32_t)staticLen);
+				offset = Native::Constant::New((int32_t)0);
 			}
-
-			return Monad::New(table, sfx.CopyData(table, tableSetter, react,true,true,true));
 		}
 
-		CTRef Transaction::SideEffects(SideEffectTransform& sfx) const {
-			auto memMonad = GetAccessor(sfx(GetUp(0)))->Cast<Monad>();
-			CTRef idx = GetAccessor(DereferenceAll(sfx(GetUp(1))));
+		CTRef SubArray::SideEffects(Backends::SideEffectTransform& sfx) const { 
+			CTRef ptr = nullptr, offset = nullptr, limit = nullptr;
+			
+			auto vec(sfx(GetUp(0)));
+			auto skip = sfx(GetUp(1));
+			GetSliceBounds(sfx, src.sourceLen, src.GetElementType(), GetReactivity(), vec, ptr, offset, limit);
+
+			skip = NI32(Add, skip, offset);
+			skip = MakeConversionNode<int64_t, Native::ToInt64>(skip, Type::Int32);
+
+			auto zero = Native::Constant::New((int64_t)0);
+			auto skipBelowZero = NI64(Min, zero, skip);
+			auto skipAboveZero = NI64(Max, zero, skip);
+
+				limit = MakeConversionNode<int64_t, Native::ToInt64>(limit, Type::Int32);
+			offset = MakeConversionNode<int64_t, Native::ToInt64>(offset, Type::Int32);			
 
-			idx = Native::MakeInt32("_clamp", Native::ClampIndex, idx, Native::Constant::New(int32_t(GetMemory()->GetSize())));
+			auto buf = Buffer::New(sfx, src.GetElementType().GetSize() * sliceLen, Buffer::Allocation::StackZeroed);
+			auto cSz = Native::Constant::New((int64_t)src.GetElementType().GetSize());
+			
+			auto size =
+				NI64(Max, zero,
+					NI64(Mul, cSz,
+						NI64(Min,
+							NI64(Add, Native::Constant::New((int64_t)sliceLen), skipBelowZero),
+							NI64(Sub, limit, skipAboveZero))));
+
+			auto cpy =
+				Copy::New(
+					Offset::New(buf,
+								NI64(Mul,
+									 skipBelowZero,
+									 Native::MakeInt64("neg", Native::Neg, cSz))),
+					Offset::New(GetAccessor(ptr),
+								NI64(Mul, skipAboveZero, cSz)),
+					size, Copy::MemCpy, GetReactivity(), 1, false, true);
+
+			return DataSource::New( 
+				Deps::New(buf, cpy),
+				Reference::New(Native::Constant::New(FixedResult(), nullptr)));
+		} 
 
-			auto sfxValue = DataSource::New(
-				AtIndex::New(Type::Chain(GetMemory()->GetElementType(), GetMemory()->GetSize(), Type()), memMonad, idx),
-				Reference::New(Native::Constant::New(GetMemory()->GetElementType(), nullptr)));
+		static CTRef MakeSlice(Backends::SideEffectTransform& sfx, CTRef ptr, CTRef offset, CTRef length, CRRef rx) {
+			auto rawSliceTy = Type::Tuple(Type::Int64, Type::Int32, Type::Int32);
+			auto sliceBuf = Buffer::New(sfx, rawSliceTy, Buffer::Stack);
+
+			auto int64sz = Native::Constant::New((int64_t)Type::Int64.GetSize());
+			auto int32sz = Native::Constant::New((int32_t)Type::Int32.GetSize());
+
+			auto output = Deps::New();
+			auto bufPtr = sliceBuf;
+
+			output->Connect(sliceBuf);
+
+			output->Connect(
+				Copy::New(bufPtr, ptr, int64sz, Copy::Store, rx, 1, false, true));
+			bufPtr = Offset::New(bufPtr, int64sz);
+
+
+			output->Connect(
+				Copy::New(bufPtr, offset, int32sz, Copy::Store, rx, 1, false, true));
+			bufPtr = Offset::New(bufPtr, int32sz);
 
-			sfx.AddSideEffect(sfxValue, GetUp(2), sfxValue, GetReactivity(), GetMemory()->GetElementType().GetSize());
-			return sfxValue;
+			output->Connect(
+				Copy::New(bufPtr, length, int32sz, Copy::Store, rx, 1, false, true));
+
+			return DataSource::New(
+				output,
+				Reference::New(Native::Constant::New(rawSliceTy, nullptr)));
+		}
+
+		CTRef Slice::SideEffects(Backends::SideEffectTransform& sfx) const {
+			auto vec = sfx(GetUp(0));
+			auto skip = sfx(GetUp(1)); 
+			auto take = sfx(GetUp(2));
+
+
+			CTRef limit = nullptr;
+			CTRef offset = nullptr;
+			CTRef ptr = nullptr;
+
+			GetSliceBounds(sfx, src.sourceLen, src.GetElementType(), GetReactivity(), vec, ptr, offset, limit);
+
+			auto skipOffs = NI32(Add, offset, skip);
+			auto length = NI32(Min, take, NI32(Sub, limit, skipOffs));
+
+			return MakeSlice(sfx, BitCast::New(Type::Int64, ptr), 
+							 skipOffs, length, GetReactivity());
 		}
 
 		CTRef RingBuffer::SideEffects(SideEffectTransform& sfx) const {
 			CTRef evictPtr, bufferPtr, rover, statePtr(sfx.GetLocalStatePointer());
+			CTRef bufferLen = nullptr;
 			if (len < 2) {
 				bufferPtr = evictPtr = statePtr;
 				rover = Native::Constant::New(int32_t(0));

          
@@ 1517,79 1771,152 @@ namespace K3
 					sfx(GetUp(0)), sfx.GetSymbolTable().GetInitializerReactivity(),
 					true,true,true));
 
-				evictPtr = bufferPtr = Monad::New(bufferPtr, initializer);
+				evictPtr = bufferPtr = Deps::New(bufferPtr, initializer);
 			} else {
 				/* generate ring buffer loop */
-				auto state(
-					DataSource::New(
-					statePtr,
-					Reference::New(Native::Constant::New(
-					Type::Pair(
-					Type::Chain(elementType, len, Type::Nil),
-					Type::Int32), 0))));
+				bufferPtr = statePtr;
+				
+				switch (timeBase) {
+				case smp:
+					bufferLen = Native::Constant::New((int32_t)len);
+					break;
+				case sec:
+				{
+					auto clockSig = Qxx::FromGraph(GetReactivity())
+										.OfType<Reactive::DriverNode>()
+										.SelectMember(&Reactive::DriverNode::GetID)
+										.FirstOrDefault(Type::Nil);
+
+					if (clockSig.IsNil()) {
+						bufferLen = Native::Constant::New((int32_t)1);
+					} else {
+						DriverSignature dsig(clockSig);
+						auto rateSig = Type::User(&ReactiveRateTag, dsig.GetMetadata());
+						auto rateKey = TLS::GetCurrentInstance()->Memoize(rateSig);
+						auto rateSlot = sfx.GetSymbolTable().GetIndex(rateKey);
+
+						sfx.GetSymbolTable()
+							.RegisterExternalVariable(rateSig, Type::Float32, rateKey, GlobalVarType::Configuration,
+													  { 1,1 }, Type::Nil);
+
+						bufferLen =
+							MakeConversionNode<int32_t, Native::ToInt32>(
+								Dereference::New(Configuration::New(Type::Float32, rateSlot), Type::Float32),
+								Type::Float32);
+
+						bufferLen =
+							MakeConversionNode<int64_t, Native::ToInt64>(
+								bufferLen, Type::Int32);
 
-				sfx.SetLocalStatePointer(
-					Offset::New(statePtr, 
-						Native::Constant::New(int64_t(len * elementType.GetSize() + Type::Int32.GetSize()))));
+						bufferLen = NI64(Mul,
+										 bufferLen,
+										 Native::Constant::New((int64_t)len));
+
+						bufferLen = NI64(BitShiftRight,
+										 bufferLen,
+										 Native::Constant::New((int64_t)20));
+
+						bufferLen = MakeConversionNode<int32_t, Native::ToInt32>(
+							bufferLen, Type::Int64);
+
+						bufferLen =
+							NI32(Add, Native::Constant::New((int32_t)1),
+								 bufferLen);
 
-				auto indexDs(state->Rest( ));
+						auto lenPtr = bufferPtr;
+						auto int32sz = Native::Constant::New((int64_t)Type::Int32.GetSize());
+						bufferPtr = Offset::New(bufferPtr, int32sz);
 
-				auto initIndex = Copy::New(indexDs->GetAccessor( ), Native::Constant::New(int32_t(0 - elementType.GetSize( ))),
+						bufferLen = Dereference::New(
+							Copy::New(lenPtr, bufferLen, int32sz,
+									  Copy::Store, sfx.GetSymbolTable().GetInitializerReactivity(),
+									  1, false, true),
+							Type::Int32);
+					}
+					break;
+				}
+				}
+
+				auto elSz = Native::Constant::New((int32_t)elementType.GetSize());
+				auto bufferSz = NI32(Mul, bufferLen, elSz);
+				auto indexPtr = Offset::New(bufferPtr, bufferSz);
+
+				sfx.SetLocalStatePointer(Offset::New(indexPtr, Native::Constant::New((int64_t)Type::Int32.GetSize())));
+
+				auto initIndex = Copy::New(indexPtr, Native::Constant::New(int32_t(0 - elementType.GetSize( ))),
 					Native::Constant::New(int32_t(4)), Copy::Store,
 					sfx.GetSymbolTable( ).GetInitializerReactivity( ), 1, true, true);
 
-				indexDs = DataSource::New(
-					Monad::New(indexDs->GetAccessor( ), initIndex),
-					indexDs->GetDataLayout( ));
+				indexPtr = Deps::New(indexPtr, initIndex);
 
-				auto initRbufProto = sfx.CopyData(state->First( )->First( ),
-					sfx(GetUp(0)),
-					sfx.GetSymbolTable( ).GetInitializerReactivity( ), true, true, true);
+				auto initRbufFirst = sfx.CopyData(
+					DataSource::New(bufferPtr,Reference::New(Native::Constant::New(elementType, nullptr))), 
+					sfx(GetUp(0)), 
+					sfx.GetSymbolTable().GetInitializerReactivity(), true, true, true);
 
-				auto initRbufRest = Copy::New(state->First( )->Rest( )->GetAccessor( ),
-					Monad::New(state->First( )->First( )->GetAccessor( ), initRbufProto),
-					Native::Constant::New(int32_t(elementType.GetSize( ))), Copy::MemCpy,
-					sfx.GetSymbolTable( ).GetInitializerReactivity( ), len - 1, true, true);
-
-				auto index = Dereference::New(indexDs->GetAccessor( ), Type::Int32);
-				auto newIdx(Native::MakeInt32("_add", Native::Add, index,
-					Native::Constant::New(static_cast<int32_t>(elementType.GetSize()))));
+				auto initRbufRest =
+					Copy::New(
+						Offset::New(bufferPtr, elSz),
+						Deps::New(bufferPtr, initRbufFirst),
+						MakeConversionNode<int64_t,Native::ToInt64>(elSz, Type::Int32),
+						Copy::MemCpy,
+						sfx.GetSymbolTable().GetInitializerReactivity(),
+						NI32(Sub, bufferLen, Native::Constant::New((int32_t)1)), true, true);
 
-				newIdx = Native::Select::New(newIdx, newIdx, Native::Constant::New(int32_t(0 - elementType.GetSize() * len)));
-
-				auto updatedIndex(Monad::New(newIdx, 
-							   Copy::New(indexDs->GetAccessor(), newIdx, 
-										 Native::Constant::New(int32_t(4)), Copy::Mode::Store, GetReactivity(), 1, true, false)));
-
-				auto initedRbufMemory = Monad::New(state->First( )->GetAccessor( ), initRbufRest);
+				auto index = Dereference::New(indexPtr, Type::Int32);
+				auto newIdx = NI32(Add, index, elSz);
+				newIdx = Native::Select::New(newIdx, newIdx, 
+											 NI32(Mul, 
+												  bufferLen,
+												  Native::Constant::New(int32_t(0 - elementType.GetSize()))));
 
-				bufferPtr = initedRbufMemory;
-				evictPtr =
-					Offset::New(GetAccessor(initedRbufMemory),
-					Native::MakeInt32("_add", Native::Add, updatedIndex, Native::Constant::New(int32_t(elementType.GetSize()*len))));
+				auto updatedIndex(Deps::New(newIdx, 
+							   Copy::New(indexPtr, newIdx, 
+										 Native::Constant::New(int64_t(Type::Int32.GetSize())), 
+										 Copy::Mode::Store, GetReactivity(), 1, true, false)));
 
-				rover = Native::MakeInt32("_div", Native::Div, 
-					Native::MakeInt32("_add", Native::Add, updatedIndex, Native::Constant::New(int32_t(elementType.GetSize()*len))),
-					Native::Constant::New(int32_t(elementType.GetSize())));
+				bufferPtr = Deps::New(bufferPtr, initRbufRest);
+				auto bufferOffset = NI32(Add, updatedIndex, bufferSz);
+				evictPtr = Offset::New(bufferPtr, bufferOffset);
+
+				rover = NI32(Div, bufferOffset, elSz);					
 			}
 
 			auto output(DataSource::New(evictPtr, Reference::New(Native::Constant::New(elementType, 0))));
 
 			sfx.AddSideEffect(output, GetUp(1), statePtr, GetReactivity(), elementType.GetSize());
 
-			auto outputTuple(
-				Pair::New(DataSource::New(bufferPtr, Reference::New(Native::Constant::New(Type::Chain(elementType, len, Type(false)), 0))),
-				Pair::New(output, rover)));
+			CTRef bufferView;
+			switch (timeBase) {
+			case smp:
+				bufferView = DataSource::New(
+					bufferPtr,
+					Reference::New(
+						Native::Constant::New(
+							Type::List(elementType, len), 0)));
+				break;
+			case sec:
+				bufferView =
+					MakeSlice(sfx,
+							  BitCast::New(Type::ArrayView(elementType), bufferPtr),
+							  Native::Constant::New((int32_t)0),
+							  bufferLen, GetReactivity());
+				break;
+			}
+
+			auto outputTuple =
+				Pair::New(bufferView,
+						  Pair::New(output, rover));
 
 			return outputTuple;
 		}
 
-		/* identity transform that removes monads */
+		/* identity transform that removes Depss */
 		class CalleeArgumentMap : public PartialTransform<Transform::Identity<const Typed>> {
 		public:
 			CalleeArgumentMap(CTRef root):PartialTransform(root) { }
 			CTRef operate(CTRef src) {
-				if (IsOfExactType<Monad>(src)) return operate(src->GetUp(0));
+				if (IsOfExactType<Deps>(src)) return operate(src->GetUp(0));
 				else return PartialTransform::operate(src);
 			}
 		};

          
@@ 1608,11 1935,11 @@ namespace K3
 			}
 		}
 
-		static CTRef SetupParameterLeaf(SideEffectTransform& sfx, CTRef datum, size_t& ParamID, std::vector<CTRef>& callerParams, bool potentialTailCall) {
+		static CTRef SetupParameterLeaf(SideEffectTransform& sfx, CTRef datum, size_t& ParamID, std::vector<CTRef>& callerParams, bool input, bool potentialTailCall) {
 			if (IsPair(datum)) {
 				return Pair::New(
-					SetupParameterLeaf(sfx, datum->GraphFirst(), ParamID, callerParams, potentialTailCall),
-					SetupParameterLeaf(sfx, datum->GraphRest(), ParamID, callerParams, potentialTailCall));
+					SetupParameterLeaf(sfx, datum->GraphFirst(), ParamID, callerParams, input, potentialTailCall),
+					SetupParameterLeaf(sfx, datum->GraphRest(), ParamID, callerParams, input, potentialTailCall));
 			} else if (MayHaveData(datum)) {
 				DataSource *buf_ds;
 				IFixedResultType *layout;

          
@@ 1622,19 1949,19 @@ namespace K3
 						return GetDereferencedParams(sfx, layout->FixedResult(), datum, callerParams, ParamID);
 				} else {
 					callerParams.push_back(sfx.GetDataAccessor(datum));
-					return DataSource::New(SubroutineArgument::In(ParamID++, datum), sfx.GetDataLayout(datum));
+					return DataSource::New(SubroutineArgument::New(input, ParamID++, datum), sfx.GetDataLayout(datum));
 				}
 			} else {
 				return Typed::Nil();
 			}
 		}
 
-		static CTRef GetCallerAndCalleeParams(SideEffectTransform& sfx, CTRef pgraph, size_t& ParamID, std::vector<CTRef>& callerParams, bool potentialTailCall = false) {
+		static CTRef GetCallerAndCalleeParams(SideEffectTransform& sfx, CTRef pgraph, size_t& ParamID, std::vector<CTRef>& callerParams, bool input, bool potentialTailCall) {
 			if (IsPair(pgraph)) {
-				auto f = GetCallerAndCalleeParams(sfx, pgraph->GraphFirst(), ParamID, callerParams, potentialTailCall);
-				auto r = GetCallerAndCalleeParams(sfx, pgraph->GraphRest(), ParamID, callerParams, potentialTailCall);
+				auto f = GetCallerAndCalleeParams(sfx, pgraph->GraphFirst(), ParamID, callerParams, input, potentialTailCall);
+				auto r = GetCallerAndCalleeParams(sfx, pgraph->GraphRest(), ParamID, callerParams, input, potentialTailCall);
 				return Pair::New(f, r);
-			} else return SetupParameterLeaf(sfx, sfx(pgraph), ParamID, callerParams, potentialTailCall);
+			} else return SetupParameterLeaf(sfx, sfx(pgraph), ParamID, callerParams, input, potentialTailCall);
 		}
 
 		static bool IsLayoutSimilar(CTRef a, CTRef b) {

          
@@ 1791,7 2118,7 @@ namespace K3
 				}
 			}
 			sfx.SetStatePointer(md);
-			return Monad::New(result, md);
+			return Deps::New(result, md);
 		}
 
 

          
@@ 1805,19 2132,19 @@ namespace K3
 			//CalleeArgumentMap CalleeParams(GetUp(0)), CalleeResults(result);
 
 			std::vector<CTRef> CallerParams, CallerResults;
-			CTRef ArgumentGraph = GetCallerAndCalleeParams(sfx, GetUp(0), ParamID, CallerParams);
+			CTRef ArgumentGraph = GetCallerAndCalleeParams(sfx, GetUp(0), ParamID, CallerParams, true, false);
 
 			if (ParamID > 8) {
 				CallerParams.clear(); ParamID = 1;
 				ArgumentGraph = GetCallerAndCalleeParams(sfx, 
-					Canonicalize(sfx(GetUp(0)), argumentReactivity, ArgumentType(), true, false, sfx), ParamID, CallerParams);
+					Canonicalize(sfx(GetUp(0)), argumentReactivity, ArgumentType(), true, false, sfx), ParamID, CallerParams, true, false);
 			}
 
-			CTRef ResultGraph = GetCallerAndCalleeParams(sfx, result, ParamID, CallerResults);
+			CTRef ResultGraph = GetCallerAndCalleeParams(sfx, result, ParamID, CallerResults, false, false);
 
 			auto subroutine(Subroutine::New(GetLabel(), Typed::Nil()));
 
-			sfx.CompileSubroutineAsync(subroutine, body,
+			sfx.CompileSubroutineAsync(label.c_str(), ArgumentType(), FixedResult(), subroutine, body,
 				SubstituteTypeToArgumentGraph(ArgumentGraph, Native::Constant::New(argumentType, 0)),
 				SubstituteTypeToArgumentGraph(ResultGraph, Native::Constant::New(FixedResult(), 0)));
 

          
@@ 1830,8 2157,8 @@ namespace K3
 			subroutine->ArgumentsConnected();
 			for (auto res : CallerResults) subroutine->Connect(res);
 
-			/* return function result buffers with Monadic dependency on the subroutine call */
-			return Monad::New(result, subroutine);
+			/* return function result buffers with Depsic dependency on the subroutine call */
+			return Deps::New(result, subroutine);
 		}
 
 		RecursionBranch::RecursionBranch(CTRef counter, CTRef up, int32_t* seq, Graph<Typed> t, Graph<Generic> a, Graph<Generic> r, CTRef calleeArgs, CTRef calleeRes, const FunctionSequence* parent)

          
@@ 1866,7 2193,7 @@ namespace K3
 
 					outPrepadAvailable--;
 
-					return Monad::New(
+					return Deps::New(
 						DataSource::New(slot->GetAccessor(), prepad->GetDataLayout()),
 						sfx.CopyData(slot, sfx(p->GetUp(0)), 0, true,false,false));
 				} else {

          
@@ 1895,9 2222,9 @@ namespace K3
 			CTRef args = UsePrepadAllocation(sfx, GetUp(1));
 
 			std::vector<CTRef> CallerParams, CallerResults;
-			CTRef ArgumentGraph = GetCallerAndCalleeParams(sfx, args, ParamID, CallerParams);
+			CTRef ArgumentGraph = GetCallerAndCalleeParams(sfx, args, ParamID, CallerParams, true, false);
 
-			CTRef ResultGraph = GetCallerAndCalleeParams(sfx, Elision, ParamID, CallerResults);
+			CTRef ResultGraph = GetCallerAndCalleeParams(sfx, Elision, ParamID, CallerResults, false, false);
 
 			//std::cout << "Sequence        Argument: " << *SequenceArgument << std::endl;
 			//std::cout << "RecursionBranch Argument: " << *ArgumentGraph << std::endl;

          
@@ 1917,7 2244,9 @@ namespace K3
 
 				Subroutine *recur = Subroutine::NewRecursive("recur", Typed::Nil(), *sequenceLength);
 
-				sfx.CompileSubroutineAsync(recur, parentSeq->GetTailCall(),
+				sfx.CompileSubroutineAsync(
+					nullptr, Type::Nil, Type::Nil,
+					recur, parentSeq->GetTailCall(),
 					SubstituteTypeToArgumentGraph(ArgumentGraph, Native::Constant::New(tailArgType, 0)),
 					SubstituteTypeToArgumentGraph(ResultGraph, Native::Constant::New(tailResType, 0)));
 

          
@@ 1932,7 2261,7 @@ namespace K3
 				for (auto res : CallerResults) recur->Connect(res);
 				recur->Connect(sfx.GetDataAccessor(Counter));
 
-				return Monad::New(Elision, recur);
+				return Deps::New(Elision, recur);
 			} else {
 				/* argument or result data map has changed; peel iteration out of loop */
 				if (parentSeq->GetRepeatCount() > 1) {

          
@@ 1961,10 2290,12 @@ namespace K3
 					CallerParams.clear(); CallerResults.clear();
 					ParamID = 1;
 
-					ArgumentGraph = GetCallerAndCalleeParams(sfx, args, ParamID, CallerParams);
-					ResultGraph = GetCallerAndCalleeParams(sfx, result, ParamID, CallerResults);
+					ArgumentGraph = GetCallerAndCalleeParams(sfx, args, ParamID, CallerParams, true, false);
+					ResultGraph = GetCallerAndCalleeParams(sfx, result, ParamID, CallerResults, false, false);
 
-					sfx.CompileSubroutineAsync(recur, parentSeq->GetTailCall(),
+					sfx.CompileSubroutineAsync(
+						nullptr, Type::Nil, Type::Nil,
+						recur, parentSeq->GetTailCall(),
 						SubstituteTypeToArgumentGraph(ArgumentGraph, Native::Constant::New(tailArgType, 0)),
 						SubstituteTypeToArgumentGraph(ResultGraph, Native::Constant::New(tailResType, 0)));
 

          
@@ 1975,7 2306,7 @@ namespace K3
 					for (auto arg : CallerParams) recur->Connect(arg);
 					recur->ArgumentsConnected();
 					for (auto res : CallerResults) recur->Connect(res);
-					return Monad::New(result, recur);
+					return Deps::New(result, recur);
 				}
 			}
 			KRONOS_UNREACHABLE;

          
@@ 1995,24 2326,23 @@ namespace K3
 
 			VariantTuple *vt;
 			if (argumentLayout->Cast(vt) && vt->GetRecurrenceDelta() > 0) {
-				using namespace Nodes::Native;
 
 				CTRef passedArgsType = ReduceType(passedArgs);
 				CTRef elementType = ReduceType(vt->GetUp(0));
 
 				ResultTypeWithNoArgument rtnoa(nullptr);
-				CTRef padding = MakeInt64("umul", Mul,
+				CTRef padding = NI64(Mul,
 					Native::Constant::New((int64_t)(elementType->Result(rtnoa).GetSize() * vt->GetRecurrenceDelta())),
 					seqLen);
 
-				CTRef bufSize = MakeInt64("uadd", Add, 
+				CTRef bufSize = NI64(Add, 
 					padding, ComputeSize(passedArgsType));
 				CTRef paddedBuffer = Buffer::New(sfx, bufSize, Buffer::Stack, 16);
 				CTRef iteratorPtr = Offset::New(paddedBuffer, padding);
 
 				DataSource *iteratorDs = DataSource::New(iteratorPtr, Reference::New(passedArgsType));
 
-				return Monad::New(iteratorDs, sfx.CopyData(iteratorDs, sfx(passedArgs), 0, true, false, false));
+				return Deps::New(iteratorDs, sfx.CopyData(iteratorDs, sfx(passedArgs), 0, true, false, false));
 			} else return 0;
 		}
 

          
@@ 2038,13 2368,13 @@ namespace K3
 			size_t ParamID(1);
 
 			std::vector<CTRef> CallerParams, CallerResults;
-			CTRef ArgumentGraph = GetCallerAndCalleeParams(sfx, args, ParamID, CallerParams);
+			CTRef ArgumentGraph = GetCallerAndCalleeParams(sfx, args, ParamID, CallerParams, true, false);
 			if (ParamID > 8) {
 					CallerParams.clear(); ParamID = 1;
 					ArgumentGraph = GetCallerAndCalleeParams(sfx,
-						Canonicalize(sfx(GetUp(0)), GetReactivity(), ArgumentType(), true, false, sfx), ParamID, CallerParams);
+						Canonicalize(sfx(GetUp(0)), GetReactivity(), ArgumentType(), true, false, sfx), ParamID, CallerParams, true, false);
 			}
-			CTRef ResultGraph = GetCallerAndCalleeParams(sfx, result, ParamID, CallerResults);
+			CTRef ResultGraph = GetCallerAndCalleeParams(sfx, result, ParamID, CallerResults, false, false);
 
 			auto CounterArgument(SubroutineArgument::In(ParamID++, Type::Int32, "count"));
 

          
@@ 2068,7 2398,9 @@ namespace K3
 			CTRef body(builder.Go());
 
 			auto subroutine(Subroutine::New(GetLabel(), Typed::Nil()));
-			sfx.CompileSubroutineAsync(subroutine, body,
+			sfx.CompileSubroutineAsync(
+				label, ArgumentType(), FixedResult(),
+				subroutine, body,
 				SubstituteTypeToArgumentGraph(ArgumentGraph, typedArgument),
 				SubstituteTypeToArgumentGraph(ResultGraph, typedResult), true);
 

          
@@ 2081,9 2413,25 @@ namespace K3
 			for (auto res : CallerResults) subroutine->Connect(res);
 			subroutine->Connect(Native::Constant::New(std::int32_t(0))); // recursion counter
 
-			/* return function result buffers with Monadic dependency on the subroutine call */
-			return Monad::New(result, subroutine);
+			/* return function result buffers with Depsic dependency on the subroutine call */
+			return Deps::New(result, subroutine);
 		}
 	};
 #pragma endregion 
+	CTRef Canonicalize(CTRef data, CRRef r, const Type& e, bool referenced, bool mustCopy, SideEffectTransform& sfx) {
+		if (mustCopy == false) {
+			const DataSource* ds;
+			data = DereferenceAll(data);
+			if (data->Cast(ds) && ds->HasPairLayout() == false && IsFused(r)) {
+				if (referenced) {
+					if (ds->CanTakeReference()) return ds->Reference();
+				} else return ds;
+			}
+		}
+		auto buf(Buffer::New(sfx, e.GetSize(), Buffer::Stack));
+		auto buf_ds(DataSource::New(buf, Reference::New(Native::Constant::New(e, 0))));
+		auto val = Deps::New(buf_ds, sfx.CopyData(buf_ds, data, r, true, false, false));
+		if (!referenced) return DereferenceOnce(val);
+		else return val;
+	}
 };

          
M src/backends/SideEffectCompiler.h +2 -2
@@ 101,7 101,7 @@ namespace K3 {
 
 			void AddSideEffect(CTRef WritePtr, CTRef WriteVal, CTRef ReadPtr, CRRef Reactivity, std::int64_t sizeIfKnown = 0);
 			
-			static Graph<Typed> Compile(IInstanceSymbolTable& symbols, const CTRef body, const CTRef args, const CTRef results);
+			static Graph<Typed> Compile(IInstanceSymbolTable& symbols, const CTRef body, const CTRef args, const CTRef results, const char *l, const Type& argTy, const Type& resTy);
 			static Graph<Typed> Compile(IInstanceSymbolTable& symbols, CTRef body, const Type& args, const Type& results); // unity-mapped, for C-callability
 
 			CTRef AllocateBuffer(CTRef size);

          
@@ 109,7 109,7 @@ namespace K3 {
 			CopyElisionTransform& GetElision() { return elision; }
 			CTRef GetArgument() { return arguments; }
 
-			void CompileSubroutineAsync(Subroutine*, CTRef, CTRef, CTRef, bool synchronous = false);		
+			void CompileSubroutineAsync(const char* l, const Type&, const Type&, Subroutine*, CTRef, CTRef, CTRef, bool synchronous = false);		
 
 		};
 	};

          
M src/common/ssstring.cpp +10 -14
@@ 160,7 160,6 @@ class simple_string : public abstract_st
 	CRef<char_buffer> content;
 	size_t content_start;
 	size_t content_end;
-	size_t _hash;
 
 #define NATVIS_CTOR
 #ifdef _MSC_VER

          
@@ 184,7 183,6 @@ class simple_string : public abstract_st
 			tmp->push_back(*data++);
 		}
 		content_end = tmp->size();
-		make_hash();
 		content = std::move(tmp);
 
 		NATVIS_CTOR

          
@@ 195,19 193,17 @@ class simple_string : public abstract_st
 		u8len=0;
 		for(size_t i(content_start);i!=content_end;++i) if ((content->at(i)&0xc0)!=0x80) u8len++;
 		assert(content_end<=content->size() && "simple_string out of bounds");
-		make_hash();
 
 		NATVIS_CTOR
 	}
 
-	void make_hash()
-	{
-		_hash = 1;
-		for(size_t i(content_start);i!=content_end;++i) _hash = (1 + _hash) * 2654435761;
-	}
 public:
 
-	size_t hash() const {return _hash;}
+	size_t hash(size_t seed) const {
+		size_t h = seed;
+		for (size_t i(content_start); i != content_end; ++i) h = (1 + h) * 2654435761;
+		return h;
+	}
 
 	static Ref<simple_string> cons(const char *data) {return new simple_string(data);}
 

          
@@ 306,7 302,8 @@ class composite_string : public abstract
 	friend class abstract_string;
 	friend class string_iterator;
 	CRef<abstract_string> lhs,rhs;
-	composite_string(const abstract_string* lhs, const abstract_string* rhs):lhs(lhs),rhs(rhs) {}
+	composite_string(const abstract_string* lhs, const abstract_string* rhs):lhs(lhs),rhs(rhs) {
+	}
 protected:
 	const simple_string* segment_at(size_t &offset) const 
 	{

          
@@ 367,9 364,8 @@ public:
 		rhs->stream(out);
 	}
 
-	size_t hash() const
-	{
-		return ((lhs->hash() + 1) * 2654435761) + rhs->hash();
+	size_t hash(size_t seed) const {
+		return rhs->hash(lhs->hash(seed));
 	}
 };
 

          
@@ 388,7 384,7 @@ CRef<abstract_string> abstract_string::u
 	return a->compare(*b)<0;
 	}};*/
 
-	struct hsh{size_t operator()(const abstract_string* a) const {return a->hash();}};
+	struct hsh{size_t operator()(const abstract_string* a) const {return a->hash(1);}};
 	struct cmp{bool operator()(const abstract_string* a, const abstract_string *b) const {return a->compare(*b)==0;}};
 	static unordered_set<CRef<abstract_string>,hsh,cmp> memoized_strings;
 

          
M src/common/ssstring.h +2 -2
@@ 47,7 47,7 @@ public:
 	virtual int compare(const abstract_string& rhs) const = 0;
 	virtual int compare(const simple_string& rhs) const = 0;
 	virtual void stream(std::ostream& out) const = 0;
-	virtual size_t hash() const = 0;
+	virtual size_t hash(size_t seed = 1) const = 0;
 	string_iterator seek(size_t offset);
 	string_iterator begin() {return seek(0);}
 	string_iterator end() {return seek(asciilen());}

          
@@ 69,5 69,5 @@ static bool operator!=(CRef<SString>& a,
 
 namespace std
 {
-	template <> struct hash<Ref<SString>> { size_t operator()(Ref<SString> s) const {return s->hash();} };
+	template <> struct hash<Ref<SString>> { size_t operator()(Ref<SString> s) const {return s->hash(1);} };
 };

          
M src/driver/LanguageServer.cpp +1 -6
@@ 652,11 652,6 @@ namespace Kronos {
 						sym = sym.substr(lastColon + 1);
 					}
 
-					auto remainder = c.first;
-					if (remainder.compare(0, prefix.length(), prefix) == 0) {
-						remainder = remainder.substr(prefix.length());
-					}
-
 					switch (c.second.kind) {
 					default:
 						completionItems.emplace_back(object{

          
@@ 669,7 664,7 @@ namespace Kronos {
 						completionItems.emplace_back(object{
 							{ "label", c.first },
 							{ "kind", (double)c.second.kind },
-							{ "insertText", remainder + "(" + c.second.argSnippet + ")" },
+							{ "insertText", c.first + "(" + c.second.argSnippet + ")" },
 							{ "insertTextFormat", 2.0 },
 							{ "sortText", sym },
 							{ "documentation", c.second.doc }

          
M src/driver/ReplEnvironment.cpp +6 -15
@@ 20,7 20,7 @@ namespace Kronos {
             std::vector<GenericGraph> Compiler::Parse(const std::string& fragment, ChangeCallback changeCallback) {
 				std::vector<GenericGraph> results;
 				{
-					LGuard lg(contextLock);
+					std::lock_guard<std::recursive_mutex> lg(contextLock);
 					cx.Parse(fragment.c_str(), true, [&results](const char *sym, GenericGraph imm) {
 						results.emplace_back(std::move(imm));
 					});

          
@@ 156,7 156,7 @@ namespace Kronos {
 			}
 
 			void Compiler::Invalidate(std::int64_t buildTy, BuildFlags flags) {
-				LGuard lg(contextLock);
+				std::lock_guard<std::recursive_mutex> lg(contextLock);
 				buildCache.update_in({ buildTy, flags }, [](auto v) {
 					return pcoll::none{};
 				});

          
@@ 208,14 208,14 @@ namespace Kronos {
 						if (buildTask.closureUid == 0) {
 							return;
 						}
-						LGuard lg{ contextLock };
+						std::lock_guard<std::recursive_mutex> lg{ contextLock };
 						Compile(buildTask);
 					}
 				});
 			}
 
 			int64_t Compiler::UID(const Type& t) {
-				LGuard lg(contextLock);
+				std::lock_guard<std::recursive_mutex> lg(contextLock);
 				return cx.UIDFromType(t);
 			}
 

          
@@ 368,18 368,9 @@ namespace Kronos {
 				auto usec = Runtime::MicroSecTy(dur);
 				Runtime::VirtualTimePoint() = Runtime::TimePointTy(usec);
 				Runtime::ScriptContext sc(Runtime::Frozen);
-
-
-				class_->construct(instanceMemory, useHost, closureData);
+				Connect(class_, instanceMemory, {});
+				class_->construct(instanceMemory, closureData);
 				class_->eval(instanceMemory, closureData, result.data.data());
-/*				for (int i = 0; i < class_->num_symbols;++i) {
-					auto &s{ class_->symbols[i] };
-					if (strcmp(s.sym, "#:VM:World:World") == 0) {
-						*class_->var(instanceMemory, s.slot_index) = &world;
-						s.process(instanceMemory, alloca(class_->result_type_size), 1);
-					}
-				}*/
-
 				result.descriptor = class_->result_type_descriptor;
 				return result;
 			} catch (...) {

          
M src/driver/ReplEnvironment.h +5 -4
@@ 50,7 50,8 @@ namespace Kronos {
 			using BuildPostProcessor = std::function<void(ClassCode&)>;
 
 			class Compiler : public Runtime::IBuilder {
-				std::mutex workQueueLock, contextLock;
+				std::mutex workQueueLock;
+				std::recursive_mutex contextLock;
 				Kronos::Context& cx;
 				using LGuard = std::lock_guard<std::mutex>;
 				std::condition_variable workAvailable;

          
@@ 105,20 106,20 @@ namespace Kronos {
 				int64_t NilUID() const { return 0ull; }
 				int64_t UID(const Type&);
 				void ExportLibraryMetadata(std::ostream& json) {
-					LGuard lg(contextLock);
+					std::lock_guard<std::recursive_mutex> lg(contextLock);
 					cx.GetLibraryMetadataAsJSON(json);
 				}
 
 				void Invalidate(int64_t buildGraphTy, BuildFlags);
 
 				void SetDebugTrace(std::string flt) {
-					LGuard lg(contextLock);
+					std::lock_guard<std::recursive_mutex> lg(contextLock);
 					cx.SetCompilerDebugTraceFilter(flt.c_str());
 				}
 
 				std::string GetSourceFilePosition(const char *token) {
 					if (!token) return "";
-					LGuard lg(contextLock);
+					std::lock_guard<std::recursive_mutex> lg(contextLock);
 					return cx.GetModuleAndLineNumberText(token);
 				}
 			private:

          
M src/driver/TestInstrumentation.cpp +1 -1
@@ 125,7 125,7 @@ public:
 	}
 	
 	void Subscribe(const MethodKey& mk, const ManagedRef& ref, krt_instance i, krt_process_call proc, const void** slot) override {
-		if (mk == MethodKey{ "(audio Rate)" }) {
+		if (mk == MethodKey{ "#Rate{audio}" }) {
 			*slot = &dumpRate;
 		} else if (mk == MethodKey{ "audio" }) {
 			dumpAudioThread = [=]() { DumpProc(i, proc); };

          
M src/driver/clierror.cpp +1 -7
@@ 25,13 25,7 @@ std::string UnwrapException(const char* 
 			out << em;
 		}
 	} else {
-		const char unbound[] = "Unbound{";
-		if (strncmp(txt, unbound, sizeof(unbound) - 1) == 0) {
-			pos = strchr(txt, ' ');
-			out << "Unbound symbol '" << std::string(txt + sizeof(unbound) - 1, pos) << "'";
-		} else {
-			out << txt;
-		}
+		out << txt;
 	}
 	return out.str();
 }

          
M src/driver/krpc.cpp +1 -0
@@ 50,6 50,7 @@ int main( int argn, const char *carg[] )
 		}
 
 		std::clog << "KRPC; Kronos " KRONOS_PACKAGE_VERSION " RPC Endpoint \n(c) 2018 Vesa Norilo, University of the Arts Helsinki\n\n";
+		std::clog << "[" << repo << " " << repoVersion << "]\n";
 
 		cx = CreateContext(Packages::BitbucketClient::ResolverCallback, &bbClient);
 

          
M src/k3/CompilerNodes.h +54 -22
@@ 4,7 4,7 @@ 
 #include "Stateful.h"
 #include "TypeAlgebra.h"
 #include "Errors.h"
-
+#include "Native.h"
 
 namespace K3 {
 	namespace Backends {

          
@@ 25,7 25,7 @@ namespace K3 {
 		END
 
 		/* metadata node for combining high level expression representation with a known pointer traversal path */
-		TYPED_NODE(DataSource,TypedBinary)
+		TYPED_NODE(DataSource, TypedBinary)
 			DataSource(CTRef accessPath, CTRef dataLayout):TypedBinary(accessPath,dataLayout){}
 		PUBLIC
 			bool CanTakeReference() const;

          
@@ 53,7 53,7 @@ namespace K3 {
 			virtual int GetAlignment() const = 0;
 		};
 
-		TYPED_NODE(SubroutineArgument,DisposableTypedLeaf,IFixedResultType,IAlignmentTrackerNode)
+		TYPED_NODE(SubroutineArgument,DisposableTypedLeaf,IAlignmentTrackerNode)
 			Type type;
 			size_t ID;
 			int pointerAlignment;

          
@@ 63,26 63,30 @@ namespace K3 {
 			SubroutineArgument(size_t ID, const char *l, bool isOutput, const Type& t, bool isReference, int pointerAlignment)
 				:ID(ID),isOutput(isOutput),type(t),isReference(isReference),label(l),pointerAlignment(pointerAlignment) {}
 		PUBLIC
-			DEFAULT_LOCAL_COMPARE(DisposableTypedLeaf,ID,isOutput,pointerAlignment,type);
+			DEFAULT_LOCAL_COMPARE(DisposableTypedLeaf,ID,isOutput,isReference,pointerAlignment,type);
 			CODEGEN_EMITTER
 			
 			unsigned ComputeLocalHash() const override;
-			static CTRef In(size_t ID, CTRef fromGraph, const char *l = "in");
+			static CTRef In(size_t ID, CTRef fromGraph, const char *l = "in") {
+				return New(true, ID, fromGraph, l);
+			}
+			static CTRef Out(size_t ID, CTRef fromGraph, const char *l = "out") {
+				return New(false, ID, fromGraph, l);
+			}
 			static CTRef In(size_t ID, const Type& t, const char *l = "in") 
 			{
 				assert(t.IsNativeType());
 				return new SubroutineArgument(ID+1,l,false,t,false,0);
 			}
+			static CTRef New(bool isInput, size_t ID, CTRef fromGraph, const char *l = nullptr);
 			static SubroutineArgument* Out(size_t ID, const Type& t, const char *l = "out") {return new SubroutineArgument(ID+1,l,true,t,false,0);}
 			static SubroutineArgument* In(size_t ID, int align, const char *l = "in") {return new SubroutineArgument(ID+1,l,false,Type::Nil,true,align);}
 			static SubroutineArgument* Out(size_t ID, int align, const char *l = "out") {return new SubroutineArgument(ID+1,l,true,Type::Nil,true,align);}
 			static SubroutineArgument* State() {return new SubroutineArgument(1,"state",false,Type::Nil,true,4);}
 			static SubroutineArgument* Self() {return new SubroutineArgument(0,"self",false,Type::Nil,true,16);}
 			Type Result(ResultTypeTransform&) const override {return type;}
-			Type FixedResult() const override {return type;}
 			void Output(std::ostream& strm) const override;
 			
-
 			size_t GetID() const {return ID;}
 			bool IsReference() const {return isReference;}
 			bool IsOutput() const {return isOutput;}

          
@@ 163,6 167,7 @@ namespace K3 {
 				PUBLIC
 				enum Allocation {
 				Stack,
+				StackZeroed,
 				Module,
 				Empty
 			};

          
@@ 216,7 221,9 @@ namespace K3 {
 			VariantTuple(CTRef elementType, CTRef elementCount, CTRef tail, int recurrenceDelta):TypedTernary(elementType,elementCount,tail),recurrenceDelta(recurrenceDelta){}
 		PUBLIC
 			DEFAULT_LOCAL_COMPARE(TypedTernary,recurrenceDelta);
-			static VariantTuple* New(CTRef element, CTRef count, CTRef tail, int delta) {return new VariantTuple(element,count,tail,delta);}
+			static VariantTuple* New(CTRef element, CTRef count, CTRef tail, int delta) {
+				return new VariantTuple(element,count,tail,delta);
+			}
 			Type Result(ResultTypeTransform&) const override {INTERNAL_ERROR("not applicable");}
 			CTRef GraphFirst() const override;
 			CTRef GraphRest() const override;

          
@@ 280,33 287,38 @@ namespace K3 {
 			int SchedulingPriority() const override { return 5; }
 		END
 	
-		TYPED_NODE(Copy,TypedTernary)
+		TYPED_NODE(Copy,TypedPolyadic)
 		PUBLIC
 			enum Mode{
 				MemCpy,
 				Store
 			};
 		PRIVATE
-			size_t repeat;
 			Mode mode;
 			int dstAlign = 4, srcAlign = 4;
 			bool mutatesState;
 			bool doesInit;
-			Copy(CTRef dst, CTRef src, CTRef size, Mode m, const Reactive::Node *r, size_t repeat, int sa, int da, bool mutate, bool init)
-				:TypedTernary(dst, src, size), repeat(repeat), mode(m), mutatesState(mutate),srcAlign(sa),dstAlign(da),doesInit(init) {
+			Copy(CTRef dst, CTRef src, CTRef size, CTRef repeat, Mode m, const Reactive::Node *r, int sa, int da, bool mutate, bool init)
+				:mode(m), mutatesState(mutate),srcAlign(sa),dstAlign(da),doesInit(init) {
+				Connect(dst); Connect(src); Connect(size); Connect(repeat);
 				SetReactivity(r);
 			}
 		PUBLIC
-			DEFAULT_LOCAL_COMPARE(TypedTernary,mode,repeat,mutatesState,doesInit);
+			DEFAULT_LOCAL_COMPARE(TypedPolyadic,mode,mutatesState,doesInit);
 			CODEGEN_EMITTER
-			static Copy* New(CTRef dst, CTRef src, CTRef sizeInBytes, Mode mode, const Reactive::Node* reactivity, size_t repeat, bool mutateState, bool doesInitialization) {
+			static Copy* New(CTRef dst, CTRef src, CTRef sizeInBytes, Mode mode, const Reactive::Node* reactivity, CTRef repeat, bool mutateState, bool doesInitialization) {
 				int sa = 0, da = 0;
 				IAlignmentTrackerNode *at;
 				if (src->Cast(at)) sa = at->GetAlignment();
 				if (dst->Cast(at)) da = at->GetAlignment();
-				return new Copy(dst,src,sizeInBytes,mode,reactivity,repeat,sa,da,mutateState,doesInitialization); 
+				return new Copy(dst,src,sizeInBytes,repeat,mode,reactivity,sa,da,mutateState,doesInitialization); 
 			}
-			Type Result(ResultTypeTransform&) const override {INTERNAL_ERROR("not implemented");}
+			static Copy* New(CTRef dst, CTRef src, CTRef sizeInBytes, Mode mode, const Reactive::Node* reactivity, size_t repeat, bool mutateState, bool doesInitialization) {
+				return New(dst, src, sizeInBytes, mode, reactivity, Native::Constant::New((int32_t)repeat), mutateState, doesInitialization);
+			}
+
+			Type Result(ResultTypeTransform&) const { return Type::Nil; }
+
 			void Output(std::ostream&) const override;
 
 			bool DoesMutateState() const { return mutatesState; }

          
@@ 315,19 327,39 @@ namespace K3 {
 			int SchedulingPriority() const override { return 5; }
 		END
 
-		TYPED_NODE(Monad,TypedPolyadic)
+		TYPED_NODE(OnInit, TypedBinary)
+			OnInit(CTRef init, CTRef stream) :TypedBinary(init, stream) {}
+		PUBLIC
+			static OnInit* New(CTRef onInit, CTRef onStream) { return new OnInit(onInit, onStream); }
+			Type Result(ResultTypeTransform& rt) const override { return GetUp(0)->Result(rt); }
+			CODEGEN_EMITTER
+		END
+
+		TYPED_NODE(Configuration, DisposableTypedLeaf, IFixedResultType)
+			int slotIndex;
+			Type ty;
+			Configuration(Type t, int si) :slotIndex(si), ty(t) {}
+		PUBLIC
+			static Configuration* New(Type ty, int slotIndex) { return new Configuration{ ty, slotIndex }; }
+			Type Result(ResultTypeTransform&) const { return ty; }
+			Type FixedResult() const { return ty; }
+			int GetSlotIndex() const { return slotIndex; }
+			CODEGEN_EMITTER
+		END
+
+		TYPED_NODE(Deps,TypedPolyadic)
 			void ConnectUpstream() {};
 			template <typename... ARGS> void ConnectUpstream(CTRef up, ARGS... upstream) {
 				Connect(up);ConnectUpstream(upstream...);
 			}
 			bool isDataProtector;
-			Monad(bool dp = false):isDataProtector(dp) { }
+			Deps(bool dp = false):isDataProtector(dp) { }
 		PUBLIC
 			DEFAULT_LOCAL_COMPARE(TypedPolyadic,isDataProtector);
 			CODEGEN_EMITTER
 			void Connect(CTRef upstream);
-			static Monad* New(bool dataProtector = false) {return new Monad(dataProtector);}
-			static CTRef Transfer(CTRef upstream, const Monad *dependencies);
+			static Deps* New(bool dataProtector = false) {return new Deps(dataProtector);}
+			static CTRef Transfer(CTRef upstream, const Deps *dependencies);
 
 			template <typename... ARGS> static CTRef New(CTRef initial, ARGS... upstream) {
 				DataSource *ds;

          
@@ 338,7 370,7 @@ namespace K3 {
 					np->SetReactivity(initial->GetReactivity( ));
 					return np;
 				}
-				auto m(new Monad);
+				auto m(new Deps);
 				m->ConnectUpstream(initial,upstream...);
 				m->SetReactivity(initial->GetReactivity( ));
 				return m;

          
@@ 349,7 381,7 @@ namespace K3 {
 				return const_cast<Typed*>(New(const_cast<CTRef>(initial),upstream...));
 			}
 
-			Type Result(ResultTypeTransform&t) const override {assert(GetNumCons() && "Incomplete Monad");return GetUp(0)->Result(t);}
+			Type Result(ResultTypeTransform&t) const override {assert(GetNumCons() && "Incomplete Deps");return GetUp(0)->Result(t);}
 			CTRef GraphFirst() const override;
 			CTRef GraphRest() const override;
 

          
M src/k3/Conversions.cpp +1 -0
@@ 78,6 78,7 @@ namespace K3 {
 		template <typename DST, int OPCODE>
 		CTRef MakeConversionNode(CTRef up, const Type& src)
 		{
+			assert(!up->Cast<IFixedResultType>() || up->Cast<IFixedResultType>()->FixedResult() == src);
 			if (src.IsFloat32()) return Cvt<DST,float,OPCODE>::New(up,1);
 			else if (src.IsFloat64()) return Cvt<DST,double,OPCODE>::New(up,1);
 			else if (src.IsInt32()) return Cvt<DST,int32_t,OPCODE>::New(up,1);

          
M src/k3/DynamicVariables.h +2 -0
@@ 19,6 19,7 @@ namespace K3 {
 			END
 
 			enum GlobalVarType {
+				Configuration,
 				Internal,
 				External,
 				Stream

          
@@ 66,6 67,7 @@ namespace K3 {
 				const Reactive::Node* ReactiveAnalyze(Reactive::Analysis&, const Reactive::Node**) const override;
 				CTRef ReactiveReconstruct(Reactive::Analysis&) const override;
 				bool IsExternal( ) const { return k == External; }
+				bool IsConfigurationSlot() const { return k == Configuration; }
 				bool IsStream() const { return k == Stream; }
 				Type GetKey() const { return key; }
 				int GetWeight() const  override { return 2; }

          
M src/k3/Evaluate.cpp +1 -23
@@ 565,23 565,7 @@ namespace K3 {
 					RegionAllocator SpecializationAttemptAllocator;
 					Specialization spec;
 
-					if (auto carg = A1.node->Cast<Native::Constant>()) {
-						struct _cst : public SpecializationTransform {
-							Specialization ConstantArgument;
-							_cst(Specialization arg, CGRef root, SpecializationDiagnostic& rep)
-								:SpecializationTransform(root, arg.result, rep), ConstantArgument(arg) { }
-
-							Specialization operate(CGRef node) {
-								if (IsOfExactType<GenericArgument>(node)) {
-									return ConstantArgument;
-								}
-								return SpecializationTransform::operate(node);
-							}
-						};
-
-						_cst ConstantPropagate{ A1, Form.GetGraph(), t.GetRep() };
-						spec = ConstantPropagate.Go();
-					} else if (recurPt) {
+					if (recurPt) {
 						struct _st : public SpecializationTransform {
 							const Type Form;
 							const std::vector<CGRef> *const recurPt;

          
@@ 663,12 647,6 @@ namespace K3 {
 						return make_pair(spec.node, spec.result);
 					}
 				} else if (Form.IsUserType()) {
-					//if (Form.IsUserType(UnboundTag)) {
-					//	auto s(TypeError(&FatalFailure));
-					//	t.GetRep().Diagnostic(LogSpecializationBranches, this, Error::EmptyGraph, "Not a function");
-					//	return std::make_pair(s.node, s.result);
-					//}
-
 					CGRef extEval = TLS::ResolveSymbol(":Fallback:Eval");
 					if (extEval) {
 						Evaluate tmp(":Fallback:Eval", extEval, GenericArgument::New());

          
M src/k3/FlowControl.cpp +54 -1
@@ 164,6 164,49 @@ namespace K3 {
 			}
 		}
 
+		Specialization GenericSlice::Specialize(SpecializationTransform& t) const {
+			SPECIALIZE_ARGS(t, 0, 1, 2);
+			auto vty = A0.result;
+			assert(A1.result.IsInt32());
+
+			Type elTy;
+			int staticLen = -1;
+
+			if (vty.IsPair()) {
+				auto head = vty.CountLeadingElements(vty.First());
+				auto tail = vty.Rest(head);
+				if (tail.IsNil() || tail == vty.First()) {
+					elTy = vty.First();								
+					staticLen = vty.Arity().GetInvariantI64();
+				} else {
+					t.GetRep().Diagnostic(LogErrors, this, Error::InvalidType, A0.result, "Slice requires a homogeneous tuple or list");
+					return TypeError(&FatalFailure, Type("Slice requires a homogeneous tuple or list"));
+				}
+			} else if (vty.IsArrayView()) {
+				elTy = vty.GetArrayViewElement();
+			} else {
+				t.GetRep().Diagnostic(LogErrors, this, Error::InvalidType, A0.result, "Slice requires a homogeneous tuple or list");
+				return TypeError(&FatalFailure, Type("Slice requires a homogeneous tuple or list"));
+			}
+
+			auto len = A2.result;
+			if (len.IsInvariant()) {
+				if (len.GetInvariantI64() > std::numeric_limits<int>::max()) {
+					t.GetRep().Diagnostic(LogErrors, this, Error::BadInput, A2.result, "Slice length is too long");
+					return TypeError(&FatalFailure, Type::Pair(Type("Slice length is too long:"), len));
+				}
+				auto sa = SubArray::New((int)len.GetInvariantI64(), staticLen, elTy, A0.node, A1.node);
+				return Specialization(sa, sa->FixedResult());
+			} else if (len.IsInt32()) {
+				auto sa = Slice::New(staticLen, elTy, A0.node, A1.node, A2.node);
+				return Specialization(sa, sa->FixedResult());
+			} else {
+				SPECIALIZE(t, cvtInt, Convert::New(Convert::Int32, GetUp(2)));
+				auto sa = Slice::New(staticLen, elTy, A0.node, A1.node, cvtInt.node);
+				return Specialization(sa, sa->FixedResult());
+			}
+		}
+
 		CTRef AtIndex::IdentityTransform(GraphTransform<const Typed, CTRef>& transform) const {
 			return New(vectorTy, transform(GetUp(0)), transform(GetUp(1)));
 		}

          
@@ 185,7 228,11 @@ namespace K3 {
 					return vector->GraphFirst();
 				}
 			}
-			return new AtIndex(vectorTy, vectorTy.First(), vectorTy.CountLeadingElements(vectorTy.First()), vector, index);
+			// count elements to select from
+			int count = vectorTy.CountLeadingElements(vectorTy.First());
+			if (vectorTy.Rest(count) == vectorTy.First()) ++count;
+
+			return new AtIndex(vectorTy, vectorTy.First(), count, vector, index);
 //			return new AtIndex(elementType, order, vector, index);
 		}
 

          
@@ 390,6 437,12 @@ namespace K3 {
 					GenericPair::New(maxi, idx)), zero)), lenc),
 			"vector index", "Selects an element from the homogeneous vector, clamping the index to the bounds");
 
+		pack.AddFunction("Slice", GenericSlice::New(
+			GenericFirst::New(Arg),
+			Convert::New(Convert::Int32, GenericFirst::New(GenericRest::New(Arg))),
+			GenericRest::New(GenericRest::New(Arg))
+		));
+
 		pack.AddFunction("Select", GenericConstantSelect::New(Arg,false), "vector constant-index", 
 						 "Selects an element from the vector, clamping index to the bounds. 'constant-index' must be an invariant constant, but the contents of 'vector' do not need to be homogeneous.");
 

          
M src/k3/FlowControl.h +104 -40
@@ 9,75 9,138 @@ namespace K3 {
 		GENERIC_NODE(When,GenericPolyadic)
 		public:
 			static When* New() { return new When(); }
-		END
+			END
 
-		GENERIC_NODE(Polymorphic, GenericPolyadic)
+				GENERIC_NODE(Polymorphic, GenericPolyadic)
 		public:
 			static Polymorphic* New() { return new Polymorphic; }
-		END
+			END
 
-		GENERIC_NODE(GenericCond, GenericUnary)
+				GENERIC_NODE(GenericCond, GenericUnary)
 				GenericCond(CGRef a) :GenericUnary(a) {}
 		public:
 			static GenericCond* New(CGRef conditions) { return new GenericCond(conditions); }
 			END
 
-		GENERIC_NODE(GenericDispatch, GenericUnary)
-			GenericDispatch(CGRef args) :GenericUnary(args) {}
-		PUBLIC
-			static GenericDispatch* New(CGRef args) { return new GenericDispatch(args); }
-		END
+				GENERIC_NODE(GenericDispatch, GenericUnary)
+				GenericDispatch(CGRef args) :GenericUnary(args) {}
+			PUBLIC
+				static GenericDispatch* New(CGRef args) { return new GenericDispatch(args); }
+			END
 
-		GENERIC_NODE(GenericUnionSubtype, GenericUnary)
-			int sti;
+				GENERIC_NODE(GenericUnionSubtype, GenericUnary)
+				int sti;
 			GenericUnionSubtype(CGRef data, int subTypeIndex) :sti(subTypeIndex), GenericUnary(data) {}
-		PUBLIC
-			static GenericUnionSubtype* New(CGRef data, int subTypeIndex) { return new GenericUnionSubtype(data, subTypeIndex); }
-		END
+			PUBLIC
+				static GenericUnionSubtype* New(CGRef data, int subTypeIndex) { return new GenericUnionSubtype(data, subTypeIndex); }
+			END
 
-		TYPED_NODE(UnsafePointerCast, DisposableTypedUnary, IFixedResultType)
-			UnsafePointerCast(const Type t, CTRef p) : DisposableTypedUnary(p),t(t) {}
+				TYPED_NODE(UnsafePointerCast, DisposableTypedUnary, IFixedResultType)
+				UnsafePointerCast(const Type t, CTRef p) : DisposableTypedUnary(p), t(t) {}
 			Type t;
-		PUBLIC
-			static UnsafePointerCast* New(const Type& t, CTRef ptr) { return new UnsafePointerCast(t, ptr); }
+			PUBLIC
+				static UnsafePointerCast* New(const Type& t, CTRef ptr) { return new UnsafePointerCast(t, ptr); }
 			Type Result(ResultTypeTransform&) const override { return t; }
 			Type FixedResult() const override { return t; }
 			CTRef SideEffects(Backends::SideEffectTransform& sfx) const override;
-		END
+			END
 
-		GENERIC_NODE(Raise,GenericUnary)
-			Raise(CGRef ex):GenericUnary(ex) {}
+				GENERIC_NODE(Raise, GenericUnary)
+				Raise(CGRef ex) :GenericUnary(ex) {}
 		public:
 			static Raise* New(CGRef exception) { return new Raise(exception); }
 			static Raise* NewFatalFailure(const char* msg, CGRef args = nullptr);
-		END
+			END
 
-		GENERIC_NODE(Handle,GenericBinary)
-		Handle(CGRef ptry, CGRef pcatch):GenericBinary(ptry,pcatch) {}
+				GENERIC_NODE(Handle, GenericBinary)
+				Handle(CGRef ptry, CGRef pcatch) :GenericBinary(ptry, pcatch) {}
 		public:
-			static Handle* New(CGRef tryGraph, CGRef catchGraph) { return new Handle(tryGraph,catchGraph); }
-		END
+			static Handle* New(CGRef tryGraph, CGRef catchGraph) { return new Handle(tryGraph, catchGraph); }
+			END
+
+				GENERIC_NODE(GenericGetVectorLen, GenericUnary)
+				GenericGetVectorLen(CGRef sig) :GenericUnary(sig) {}
+			PUBLIC
+				static GenericGetVectorLen* New(CGRef sig) { return new GenericGetVectorLen(sig); }
+			END
+
+				GENERIC_NODE(GenericSelect, GenericTernary)
+				GenericSelect(CGRef vector, CGRef idx, CGRef max) : GenericTernary(vector, idx, max) {}
+			PUBLIC
+				static GenericSelect* New(CGRef vec, CGRef idx, CGRef max) { return new GenericSelect(vec, idx, max); }
+			END
+
+				GENERIC_NODE(GenericConstantSelect, GenericUnary)
+				bool wrap;
+			GenericConstantSelect(CGRef a, bool w) :wrap(w), GenericUnary(a) {}
+			PUBLIC
+				DEFAULT_LOCAL_COMPARE(GenericUnary, wrap);
+			static GenericConstantSelect* New(CGRef vecidx, bool wrap) { return new GenericConstantSelect(vecidx, wrap); }
+			END
+
+				GENERIC_NODE(GenericSlice, GenericTernary)
+				GenericSlice(CGRef vec, CGRef skip, CGRef take) :GenericTernary(vec, skip, take) {}
+			PUBLIC
+				static GenericSlice* New(CGRef vec, CGRef skip, CGRef take) { return new GenericSlice(vec, skip, take); }
+			END
 
-		GENERIC_NODE(GenericGetVectorLen,GenericUnary)
-		GenericGetVectorLen(CGRef sig):GenericUnary(sig) {}
+				GENERIC_NODE(GenericSliceArity, GenericUnary)
+				GenericSliceArity(CGRef vec) : GenericUnary(vec) {}
+			PUBLIC
+				static GenericSliceArity* New(CGRef vec) { return new GenericSliceArity(vec); }
+			END
+
+
+		struct SliceSource {
+			Type elementType;
+			int sourceLen;
+			Type GetElementType() const {
+				return elementType;
+			}
+
+			Type GetSourceArrayType() const {
+				return Type::List(elementType, sourceLen);				
+			}
+		};
+
+		TYPED_NODE(Slice, DisposableTypedTernary, IFixedResultType)
+			SliceSource src;
+			Slice(int sl, Type et, CTRef vec, CTRef skip, CTRef take) 
+				:src({et, sl})
+				,DisposableTypedTernary(vec,skip,take) { }
 		PUBLIC
-			static GenericGetVectorLen* New(CGRef sig) { return new GenericGetVectorLen(sig); }
+			DEFAULT_LOCAL_COMPARE(DisposableTypedTernary, src.elementType);
+			static Slice* New(int staticLen, Type buffer, CTRef vector, CTRef skip, CTRef take) {
+				return new Slice(staticLen, buffer, vector, skip, take);
+			}
+
+			Type FixedResult() const override { return Type::ArrayView(src.GetElementType()); }
+			Type Result(ResultTypeTransform&) const override { return Type::ArrayView(src.GetElementType()); }
+			CTRef SideEffects(Backends::SideEffectTransform& sfx) const override;
 		END
 
-		GENERIC_NODE(GenericSelect,GenericTernary)
-		GenericSelect(CGRef vector, CGRef idx, CGRef max):GenericTernary(vector,idx,max) {}
+		TYPED_NODE(SubArray, DisposableTypedBinary, IFixedResultType)
+			int sliceLen;
+			SliceSource src;
+			SubArray(int len, int sl, Type et, CTRef vec, CTRef skip)
+				:DisposableTypedBinary(vec, skip)
+				, src({ et, sl })
+				, sliceLen(len) {}
 		PUBLIC
-			static GenericSelect* New(CGRef vec, CGRef idx, CGRef max) { return new GenericSelect(vec,idx,max); }
+			DEFAULT_LOCAL_COMPARE(DisposableTypedBinary, src.elementType, sliceLen);
+			static SubArray* New(int len, int srcLen, Type bt, CTRef vec, CTRef skip) {
+				return new SubArray(len, srcLen, bt, vec, skip);
+			}
+			Type FixedResult() const override {
+				return Type::Tuple(src.GetElementType(), sliceLen);
+			}
+			Type Result(ResultTypeTransform&) const override {
+				return FixedResult();
+			}
+			CTRef SideEffects(Backends::SideEffectTransform& sfx) const override;
 		END
 
-		GENERIC_NODE(GenericConstantSelect, GenericUnary)
-			bool wrap;
-			GenericConstantSelect(CGRef a,bool w) :wrap(w),GenericUnary(a) {}
-		PUBLIC
-			DEFAULT_LOCAL_COMPARE(GenericUnary, wrap);
-			static GenericConstantSelect* New(CGRef vecidx, bool wrap) { return new GenericConstantSelect(vecidx, wrap); }
-		END
-
+			
 		GENERIC_NODE(GenericTernaryAtom,GenericTernary)
 		GenericTernaryAtom(CGRef cond, CGRef whenTrue, CGRef whenFalse):GenericTernary(cond,whenTrue,whenFalse) {}
 		PUBLIC

          
@@ 100,6 163,7 @@ namespace K3 {
 			virtual int GetWeight() const override { return 2; }
 		END
 
+
 		TYPED_NODE(Switch, TypedPolyadic, IFixedResultType)
 			Type result;
 			std::vector<int> branchResultSubtypeIndex;

          
M src/k3/Invariant.cpp +21 -2
@@ 49,6 49,16 @@ namespace K3 {
 				return Specialization(Typed::Nil(),Type(A0.result == A1.result));
 			}
 
+			Specialization GenericOrdinalCompareType::Specialize(SpecializationTransform& t) const {
+				SPECIALIZE_ARGS(t, 0, 1);
+				// extract as little information from types as possible, in order to
+				// minimize the type rules created during recursive specialization
+				if (expectOrdinal == 0)
+					return Specialization(Typed::Nil(), Type(A0.result == A1.result));
+
+				return Specialization(Typed::Nil(), Type(A0.result.OrdinalCompare(A1.result) == expectOrdinal));
+			}
+
 			Specialization GenericRequire::Specialize(SpecializationState& t) const {
 				SPECIALIZE_ARGS(t,0,1);
 				return A1;

          
@@ 542,7 552,7 @@ namespace K3 {
 		Specialization GenericDependency::Specialize(SpecializationState& t) const {
 			SPECIALIZE(t, val, GetUp(0));
 			SPECIALIZE(t, effect, GetUp(1));
-			return Specialization(Monad::New(val.node, effect.node), val.result.Fix());
+			return Specialization(Deps::New(val.node, effect.node), val.result.Fix());
 		}
 
 		Specialization GenericSpecializationCallback::Specialize(SpecializationState& t) const {

          
@@ 983,7 993,16 @@ namespace K3 {
 		AddBinary(pack, "Class-Of", Invariant::GenericClassOf::New(Arg),
 						"instance","Returns a type tag describing the type class of 'instance'");
 
-		AddBinary(pack,"Equal-Type",Invariant::GenericEqualType::New(GenericFirst::New(Arg),GenericRest::New(Arg)),"a b","performs compile time deep comparison of two type structures and returns true if identical");
+		auto typeCmp = [Arg](int expectOrdinal) {
+				return Invariant::GenericOrdinalCompareType::New(
+					expectOrdinal,
+					GenericFirst::New(Arg),
+					GenericRest::New(Arg));
+		};
+
+		AddBinary(pack,"Equal-Type",typeCmp(0),"a b","performs compile time deep comparison of two type structures and returns true if identical");
+		AddBinary(pack, "Less-Type", typeCmp(-1), "a b", "provides ordering between two types at compile time");
+		AddBinary(pack, "Greater-Type", typeCmp(1), "a b", "provides ordering between two types at compile time");
 
 		pack.AddMacro("Constant",GenericTypeTag::New(&InvariantTag), false);
 		BuildInvariantStringOps(pack.AddPackage("String"));

          
M src/k3/Invariant.h +7 -0
@@ 67,6 67,13 @@ namespace K3 {
 				static GenericEqualType* New(CGRef a, CGRef b) { return new GenericEqualType(a,b); }
 			END
 
+			GENERIC_NODE(GenericOrdinalCompareType, GenericBinary)
+				int expectOrdinal;
+				GenericOrdinalCompareType(int expect, CGRef a, CGRef b) : GenericBinary(a, b), expectOrdinal(expect) {}
+			PUBLIC 
+				static GenericOrdinalCompareType* New(int expectOrdinal, CGRef a, CGRef b) { return new GenericOrdinalCompareType(expectOrdinal, a, b); }
+			END
+
 			GENERIC_NODE(ReplicateFirst,DisposableGenericBinary,IInversible)
 				ReplicateFirst(CGRef count, const Type& element, CGRef chain, int delta):DisposableGenericBinary(count,chain),element(element.Fix()),recurrenceDelta(delta) {}
 				Type element;

          
M src/k3/LibraryRef.cpp +4 -6
@@ 74,9 74,8 @@ namespace K3 {
                         }
                     }
 				}
-				std::stringstream ss;
-				ss << std::hex << std::uintptr_t(GetRepositoryAddress());
-				return Specialization(Typed::Nil(), Type::User(&UnboundTag, Type::Pair(Type(lookup.front().c_str()), Type(ss.str().c_str()))));
+				t.GetRep().Diagnostic(Verbosity::LogErrors, this, Error::UndefinedSymbol, "Undefined symbol '%s'.", lookup.front().c_str());
+				return TypeError(&FatalFailure, Type(("Unbound symbol '" + lookup.front() + "'").c_str()));
 			}
 
 			Specialization Symbol::Specialize(SpecializationState& t) const {

          
@@ 85,9 84,8 @@ namespace K3 {
 				sym.result.OutputText(symName);
 				auto s = TLS::ResolveSymbol(symName.str().c_str());
 				if (s) return t(s);
-				std::stringstream ss;
-				ss << std::hex << std::uintptr_t(GetRepositoryAddress());
-				return Specialization(Typed::Nil(), Type::User(&UnboundTag, Type::Pair(Type(symName.str().c_str()), Type(ss.str().c_str()))));
+				t.GetRep().Diagnostic(Verbosity::LogErrors, this, Error::UndefinedSymbol, "Undefined symbol '%s'.",symName.str().c_str());
+				return TypeError(&FatalFailure, Type(("Unbound symbol '" + symName.str() + "'").c_str()));
 			}
 		};
 

          
M src/k3/Native.cpp +16 -2
@@ 317,6 317,19 @@ namespace K3 {
 			//	return Get();
 			//}
 
+			template <typename T> bool TypeCheck(CTRef t, int vw = 1) {
+				if (auto c = t->Cast<IFixedResultType>()) {
+					auto t = c->FixedResult();
+					if (t.IsNativeVector()) {
+						return 
+							t.GetVectorElement() == Type::FromNative<T>() &&
+							t.GetVectorWidth() == vw;
+					}
+					return (c->FixedResult() == Type::FromNative<T>() && vw == 1);
+				}
+				return true;
+			}
+
 			template <typename T> class TBin : public  ITypedBinary {
 				REGION_ALLOC(TBin)
 				INHERIT_RENAME(TBin, ITypedBinary)

          
@@ 334,6 347,7 @@ namespace K3 {
 				}
 
 				static Typed* New(CTRef a, CTRef b, Native::Opcode op, std::uint8_t vectorWidth, T(*action)(T, T)) {
+					assert(TypeCheck<T>(a, vectorWidth) && TypeCheck<T>(b, vectorWidth));
 					Constant *ac, *bc;
 					if (a->Cast(ac) && b->Cast(bc) && ac->GetPointer() && bc->GetPointer()) {
 						T* src1 = (T*)ac->GetPointer();

          
@@ 393,6 407,7 @@ namespace K3 {
 			public:
 				DEFAULT_LOCAL_COMPARE(ITypedUnary, opcode, vec)
 				static Typed* New(CTRef a, Native::Opcode op, std::uint8_t vectorWidth, T(*action)(T)) {
+					assert(TypeCheck<T>(a, vectorWidth));
 					Constant *ac(nullptr);
 					if (action && a->Cast(ac) && ac->GetPointer()) {
 						std::vector<T> dest(vectorWidth);

          
@@ 727,7 742,7 @@ namespace K3 {
 
 				CTypes.push_back(cty);
 				KTypes.push_back(kty);
-				Connect(kty.GetSize() > 0 ? source : Typed::Nil());
+				Connect((kty.GetSize() > 0 || source->GetNumCons() == 0) ? source : Typed::Nil());
 				return true;
 			}
 

          
@@ 821,7 836,6 @@ namespace K3 {
 		pack.AddMacro("Double", GenericTypeTag::New(Type::Float64.TypeOf().GetDescriptor()), false);
 		pack.AddMacro("Int32", GenericTypeTag::New(Type::Int32.TypeOf().GetDescriptor()), false);
 		pack.AddMacro("Int64", GenericTypeTag::New(Type::Int64.TypeOf().GetDescriptor()), false);
-		pack.AddMacro("Unbound-Symbol", GenericTypeTag::New(&UnboundTag), false);
 		pack.AddMacro("Exception", GenericTypeTag::New(&UserException), false);
 
 		pack.AddMacro("Vector", GenericTypeTag::New(&VectorTag), false);

          
M src/k3/Parser.cpp +8 -6
@@ 150,7 150,7 @@ namespace K3 {
 			return destructure(defs, c, [rhs]() {return rhs;});
 		}
 
-		static Err<CGRef> MakeRingBuffer(const std::vector<CGRef>& n) {
+		static Err<CGRef> MakeRingBuffer(RingBufferTimeBase tb, const std::vector<CGRef>& n) {
 			CGRef initializer;
 			CGRef order;
 			

          
@@ 167,7 167,7 @@ namespace K3 {
 				return ParserError(nullptr, "rbuf must have two or three arguments; optional initializer, invariant order and signal input.");
 			}
 
-			auto rb = GenericRingBuffer::New(initializer, order);
+			auto rb = GenericRingBuffer::New(tb, initializer, order);
 			rb->Connect(n.back());
 			return (CGRef)rb;
 		}

          
@@ 191,15 191,17 @@ namespace K3 {
 			{ "z-1",    [](nrv n) -> Err<CGRef> {
 				GenericRingBuffer* rb;
 				switch (n.size()) {
-					case 1: rb = GenericRingBuffer::New(Invariant::Constant::New(0), Invariant::Constant::New(1)); break;
-					case 2: rb = GenericRingBuffer::New(n[0], Invariant::Constant::New(1)); break;
+					case 1: rb = GenericRingBuffer::New(smp, Invariant::Constant::New(0), Invariant::Constant::New(1)); break;
+					case 2: rb = GenericRingBuffer::New(smp, n[0], Invariant::Constant::New(1)); break;
 					default: return ParserError(nullptr, "z-1 must have one or two arguments; optional initializer and signal input."); break;
 				}
 				rb->Connect(n.back()); 
 				return GenericRingBuffer::rbuf(rb); 
 			} },
-			{ "rbuf",   [](nrv n) -> Err<CGRef> { auto rb = MakeRingBuffer(n); if (rb.err) return std::move(rb.err); else return GenericRingBuffer::rbuf(*rb); } },
-			{ "rcsbuf", [](nrv n) { return MakeRingBuffer(n); } },
+			{ "rbuf",   [](nrv n) -> Err<CGRef> { auto rb = MakeRingBuffer(smp, n); if (rb.err) return std::move(rb.err); else return GenericRingBuffer::rbuf(*rb); } },
+			{ "rcsbuf", [](nrv n) { return MakeRingBuffer(smp, n); } },
+			{ "tbuf",   [](nrv n) -> Err<CGRef> { auto rb = MakeRingBuffer(sec, n); if (rb.err) return std::move(rb.err); else return GenericRingBuffer::rbuf(*rb); } },
+			{ "tcsbuf", [](nrv n) { return MakeRingBuffer(sec, n); } },
 			{ "Specialization-Monitor", [](nrv n) -> Err<CGRef> {
 				CHECK_ARITY(2);
 				return GenericSpecializationCallback::New(n[0], n[1]);

          
M src/k3/Reactive.cpp +25 -26
@@ 14,6 14,7 @@ 
 #include "Invariant.h"
 #include "NativeVector.h"
 #include "UserErrors.h"
+#include "kronos.h"
 #include <cmath>
 #include <algorithm>
 #include <memory>

          
@@ 136,12 137,12 @@ namespace K3 {
 			return cpy;
 		}
 
-		const Node* Monad::ReactiveAnalyze(Analysis& t, const Node** upRx) const {
+		const Node* Deps::ReactiveAnalyze(Analysis& t, const Node** upRx) const {
 			if (upRx[0]) return upRx[0];
 			else return nullptr;
 		}
 
-		CTRef Monad::ReactiveReconstruct(Analysis& t) const {
+		CTRef Deps::ReactiveReconstruct(Analysis& t) const {
 			auto cpy = IdentityTransform(t);
 			const_cast<Typed*>(cpy)->SetReactivity(t.ReactivityOf(this));
 			return cpy;

          
@@ 164,14 165,6 @@ namespace K3 {
 			return cpy;
 		}
 
-		const Node* Transaction::ReactiveAnalyze(Analysis& t, const Node** upRx) const {
-			INTERNAL_ERROR("todo");
-		}
-
-		CTRef Transaction::ReactiveReconstruct(Analysis& t) const {
-			INTERNAL_ERROR("todo");
-		}
-
 		static void ReserveOutputSignalMasks(const Node* output, Analysis& a) {
 			for(auto dn : Qxx::FromGraph(output).OfType<DriverNode>()) {
 				DriverSignature dsig = dn->GetID();

          
@@ 470,7 463,7 @@ namespace K3 {
 			}
 
 			CTRef Gate::ReactiveReconstruct(Analysis &st) const {
-				auto m = Monad::New( );
+				auto m = Deps::New( );
 				int bitIdx = st.GetSignalMask(GetUp(0));
 				m->SetReactivity(st.ReactivityOf(this));
 				m->Connect(st.Boundary(st(GetUp(1)), st.ReactivityOf(GetUp(1)), m->GetReactivity( )));

          
@@ 603,24 596,28 @@ namespace K3 {
 			const Node* BaseRate::ReactiveAnalyze(Reactive::Analysis& a, const Node** upRx) const {
 				if (upRx[0] == nullptr) return nullptr;
 
-				DriverSet rateReactivity;
+				if ((TLS::GetCurrentFlags() & Kronos::BuildFlags::DynamicRateSupport) != 0) {
+					DriverSet rateReactivity;
 
-				for(auto drv : Qxx::FromGraph(upRx[0])
-								   .OfType<DriverNode>()
-								   .Select([](const DriverNode* dn) { return dn->GetID(); })
-								   .Where([](const Type& t) { return t.IsUserType(Reactive::ReactiveDriver); })) {
+					for (auto drv : Qxx::FromGraph(upRx[0])
+						 .OfType<DriverNode>()
+						 .Select([](const DriverNode* dn) { return dn->GetID(); })
+						 .Where([](const Type& t) { return t.IsUserType(Reactive::ReactiveDriver); })) {
 
-					DriverSignature sig(drv);
+						DriverSignature sig(drv);
 
-					if (sig.GetDriverClass() == DriverSignature::User) {
-						DriverSignature rateSig(Type::Tuple(sig.GetMetadata(), Type("Rate")), Type(0));
-						Type derivedId = rateSig;
-						a.GetDelegate( ).RegisterDriver(derivedId, 1, 1);
-						rateReactivity.Merge(a.GetDelegate( ), derivedId);
+						if (sig.GetDriverClass() == DriverSignature::User) {
+							DriverSignature rateSig(Type::User(&ReactiveRateTag, sig.GetMetadata()), Type(0));
+							Type derivedId = rateSig;
+							a.GetDelegate().RegisterDriver(derivedId, 1, 1);
+							rateReactivity.Merge(a.GetDelegate(), derivedId);
+						}
 					}
+
+					return a.Memoize(rateReactivity);
+				} else {
+					return a.GetLeafReactivity();
 				}
-
-				return a.Memoize(rateReactivity);
 			}
 
 			CTRef BaseRate::ReactiveReconstruct(Analysis& a) const {

          
@@ 629,14 626,16 @@ namespace K3 {
 				for (auto d : Qxx::FromGraph(a.ReactivityOf(GetUp(0))).OfType<DriverNode>( )) {
 					DriverSignature sig(d->GetID( ));
 					if (sig.GetDriverClass( ) == DriverSignature::User) {
-						auto rateSig = Type::Tuple(sig.GetMetadata(), Type("Rate"));
+						auto rateSig = Type::User(&ReactiveRateTag, sig.GetMetadata());
 						Typed* extRate = GetGlobalVariable::New(
 							TLS::GetCurrentInstance()->Memoize(rateSig),
 							Type::Float32,
 							rateSig,
 							std::make_pair(1, 1),
 							nullptr,
-							External);
+							(TLS::GetCurrentFlags() & Kronos::DynamicRateSupport) != 0 
+							? External 
+							: Configuration);
 
 						double ratio = sig.GetRatio( );
 						if (ratio != 1.0) extRate = Native::MakeFloat("mul", Native::Mul, extRate, Native::Constant::New((float)ratio));

          
M src/k3/Stateful.cpp +45 -96
@@ 9,79 9,6 @@ 
 
 namespace K3 {
 	namespace Nodes {
-		Specialization GenericTable::Specialize(SpecializationTransform& spec) const {
-			SPECIALIZE(spec, init, GetUp(0));
-			SPECIALIZE(spec, numTrans, GetUp(1));
-
-			auto msg = "Table initializer must return a nil-terminated homogenic list";
-			if (init.result.IsTuple() == false) {
-				spec.GetRep().Diagnostic(Verbosity::LogErrors, GetUp(0), Error::BadStateDataSource, msg);
-				return spec.GetRep().TypeError("Table", init.result, Type(msg));
-			}
-
-			Type et = init.result.First().Fix();
-			size_t count = init.result.CountLeadingElements(et);
-
-			if (init.result.Rest(count) != Type::Nil) {
-				spec.GetRep().Diagnostic(Verbosity::LogErrors, GetUp(0), Error::BadStateDataSource, "Table initializer must return a nil-terminated homogenic list");
-				return spec.GetRep().TypeError("Table", init.result, Type(msg));
-			}
-
-			if (numTrans.result.IsInvariant() == false) {
-				spec.GetRep().Diagnostic(Verbosity::LogErrors, GetUp(1), Error::BadStateDataSource, "Transaction count must be specified as an invariant constant");
-				return spec.GetRep().TypeError("Table", numTrans.result, Type("Transaction count must be an invariant constant"));
-			}
-			
-			Memory *mem = Memory::New(count, et, init.node);
-			vector<Transaction*> pendingTransactions((size_t)numTrans.result.GetInvariant());
-
-			CTRef transactResults = Typed::Nil();
-
-			for (auto &pt : pendingTransactions) {
-				pt = Transaction::New(1, mem, Typed::Nil(), Typed::Nil());
-				transactResults = Pair::New(pt, transactResults);
-			}
-
-			spec.QueuePostProcessing([&spec, this, init, et, pendingTransactions](Specialization finalGraph) -> Specialization {
-				SPECIALIZE(spec, transactions, GetUp(2));
-
-				const Type expectedTransTy = Type::Chain(Type::Pair(Type::Int32, et), pendingTransactions.size(), Type::Nil);
-				if (transactions.result != expectedTransTy) {
-					spec.GetRep().Diagnostic(Verbosity::LogErrors, GetUp(2), Error::BadStateDataSource, transactions.result, expectedTransTy, "Table transaction has incorrect type");
-					return spec.GetRep().TypeError("Table", transactions.result, Type::Pair(Type("Expected"), expectedTransTy));
-				}
-
-				Type tl = transactions.result.Fix();
-				CTRef transNode = transactions.node;
-
-				for (auto pt = pendingTransactions.rbegin(); pt != pendingTransactions.rend(); ++pt) {
-
-					Type t = tl.First();
-					CTRef tn = transNode->GraphFirst();
-
-					(*pt)->Reconnect(1, tn->GraphFirst());
-					(*pt)->Reconnect(2, tn->GraphRest());
-
-					transNode = transNode->GraphRest();
-					tl = tl.Rest();
-				}
-				return finalGraph;
-			});
-
-			return Specialization(Pair::New(mem, transactResults),
-				Type::Pair(mem->FixedResult(), Type::Chain(et, pendingTransactions.size(), Type::Nil)));
-		}
-
-		CGRef GenericTable::IdentityTransform(GraphTransform<const Generic, CGRef>& copy) const {
-			GenericTable *tbl = ConstructShallowCopy();
-			tbl->Reconnect(0, copy(GetUp(0)));
-			tbl->Reconnect(1, copy(GetUp(1)));
-			copy.QueuePostProcessing([tbl, &copy](CGRef g){
-				tbl->Reconnect(2, copy(tbl->GetUp(2)));
-				return g;
-			});
-			return tbl;
-		}
 
 		class InitializerSynthesisTransform : public Transform::Identity<const Generic> {
 			std::unordered_set<const GenericRingBuffer*> decycle;

          
@@ 138,8 65,14 @@ namespace K3 {
 
 			if (order.result.IsInvariant()) {
 				Type et(init.result.Fix());
-				size_t rb_order((size_t)(order.result.GetInvariant()));
-				auto s(RingBuffer::New(rb_order,init.node,et));
+
+				auto len = order.result.GetInvariant();
+
+				// fixed point resolution in ca. usec
+				if (timeBase == sec) len *= (1 << 20);
+
+				size_t fpLen = (size_t)len;
+				auto s(RingBuffer::New(timeBase, fpLen, init.node, et));
 
 				spec.QueuePostProcessing([&spec,this,s,et,init, generatedInitializer](Specialization result) -> Specialization {
 					auto sig(spec(GetUp(2)));

          
@@ 164,11 97,18 @@ namespace K3 {
 					s->Connect(sig.node);
 					return result;
 				});
+
+				Type bufferType;
+				if (timeBase == smp) {
+					if (s->GetLen() > 1) bufferType = Type::List(et, s->GetLen());
+					else bufferType = et;
+				} else {
+					bufferType = Type::ArrayView(et);
+				}
 	
 				return Specialization(s,
 					Type::Tuple(
-						s->GetLen()>1 ? Type::Chain(et,s->GetLen(),Type(false))
-									  : et,
+						bufferType,
 						et,
 						Type::Int32));
 			} else {

          
@@ 231,24 171,33 @@ namespace K3 {
 			HASHER(h,elementType.GetHash());
 			return unsigned(h);
 		}
-
-		CTRef Transaction::IdentityTransform(GraphTransform<const Typed, CTRef>& copy) const {
-			auto trns = ConstructShallowCopy();
-			trns->Reconnect(0, copy(trns->GetUp(0)));
-			copy.QueuePostProcessing([trns, &copy](CTRef g){
-				for (unsigned int i = 1; i < trns->GetNumCons(); ++i) {
-					trns->Reconnect(i, copy(trns->GetUp(i)));
-				}
-				return g;
-			});
-			return trns;
-		}
-
-		unsigned Memory::ComputeLocalHash() const {
-			unsigned h = DisposableTypedUnary::ComputeLocalHash();
-			HASHER(h, (unsigned)sz);
-			HASHER(h, (unsigned)elTy.GetHash());
-			return h;