diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index 4552a0125..3e1357498 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -722,50 +722,32 @@ std::ostream &operator<<(std::ostream &os, const wire<Bits> &val) {
 
 template<size_t Width>
 struct memory {
-	std::vector<value<Width>> data;
+	const size_t depth;
+	std::unique_ptr<value<Width>[]> data;
 
-	size_t depth() const {
-		return data.size();
-	}
-
-	memory() = delete;
-	explicit memory(size_t depth) : data(depth) {}
+	explicit memory(size_t depth) : depth(depth), data(new value<Width>[depth]) {}
 
 	memory(const memory<Width> &) = delete;
 	memory<Width> &operator=(const memory<Width> &) = delete;
 
 	memory(memory<Width> &&) = default;
-	memory<Width> &operator=(memory<Width> &&) = default;
-
-	// The only way to get the compiler to put the initializer in .rodata and do not copy it on stack is to stuff it
-	// into a plain array. You'd think an std::initializer_list would work here, but it doesn't, because you can't
-	// construct an initializer_list in a constexpr (or something) and so if you try to do that the whole thing is
-	// first copied on the stack (probably overflowing it) and then again into `data`.
-	template<size_t Size>
-	struct init {
-		size_t offset;
-		value<Width> data[Size];
-	};
-
-	template<size_t... InitSize>
-	explicit memory(size_t depth, const init<InitSize> &...init) : data(depth) {
-		data.resize(depth);
-		// This utterly reprehensible construct is the most reasonable way to apply a function to every element
-		// of a parameter pack, if the elements all have different types and so cannot be cast to an initializer list.
-		auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...};
-		(void)_;
+	memory<Width> &operator=(memory<Width> &&other) {
+		assert(depth == other.depth);
+		data = std::move(other.data);
+		write_queue = std::move(other.write_queue);
+		return *this;
 	}
 
 	// An operator for direct memory reads. May be used at any time during the simulation.
 	const value<Width> &operator [](size_t index) const {
-		assert(index < data.size());
+		assert(index < depth);
 		return data[index];
 	}
 
 	// An operator for direct memory writes. May only be used before the simulation is started. If used
 	// after the simulation is started, the design may malfunction.
 	value<Width> &operator [](size_t index) {
-		assert(index < data.size());
+		assert(index < depth);
 		return data[index];
 	}
 
@@ -790,7 +772,7 @@ struct memory {
 	std::vector<write> write_queue;
 
 	void update(size_t index, const value<Width> &val, const value<Width> &mask, int priority = 0) {
-		assert(index < data.size());
+		assert(index < depth);
 		// Queue up the write while keeping the queue sorted by priority.
 		write_queue.insert(
 			std::upper_bound(write_queue.begin(), write_queue.end(), priority,
@@ -947,9 +929,9 @@ struct debug_item : ::cxxrtl_object {
 		flags   = 0;
 		width   = Width;
 		lsb_at  = 0;
-		depth   = item.data.size();
+		depth   = item.depth;
 		zero_at = zero_offset;
-		curr    = item.data.empty() ? nullptr : item.data[0].data;
+		curr    = item.data ? item.data[0].data : nullptr;
 		next    = nullptr;
 		outline = nullptr;
 	}
@@ -1051,9 +1033,9 @@ struct debug_items {
 	}
 };
 
-// Tag class to disambiguate module move constructor and module constructor that takes black boxes
-// out of another instance of the module.
-struct adopt {};
+// Tag class to disambiguate the default constructor used by the toplevel module that calls reset(),
+// and the constructor of interior modules that should not call it.
+struct interior {};
 
 struct module {
 	module() {}
diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc
index ff28c20b3..6623e025e 100644
--- a/backends/cxxrtl/cxxrtl_backend.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -934,11 +934,6 @@ struct CxxrtlWorker {
 		f << "}";
 	}
 
-	void dump_const_init(const RTLIL::Const &data)
-	{
-		dump_const_init(data, data.size());
-	}
-
 	void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)
 	{
 		f << "value<" << width << ">";
@@ -1785,20 +1780,10 @@ struct CxxrtlWorker {
 		} else {
 			f << "<" << wire->width << ">";
 		}
-		f << " " << mangle(wire);
-		if (wire_init.count(wire)) {
-			f << " ";
-			dump_const_init(wire_init.at(wire));
-		}
-		f << ";\n";
+		f << " " << mangle(wire) << ";\n";
 		if (edge_wires[wire]) {
 			if (!wire_type.is_buffered()) {
-				f << indent << "value<" << wire->width << "> prev_" << mangle(wire);
-				if (wire_init.count(wire)) {
-					f << " ";
-					dump_const_init(wire_init.at(wire));
-				}
-				f << ";\n";
+				f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n";
 			}
 			for (auto edge_type : edge_types) {
 				if (edge_type.first.wire == wire) {
@@ -1848,38 +1833,65 @@ struct CxxrtlWorker {
 		f << "value<" << wire->width << "> " << mangle(wire) << ";\n";
 	}
 
-	void dump_memory(Mem *mem)
+	void dump_reset_method(RTLIL::Module *module)
 	{
-		dump_attrs(mem);
-		f << indent << "memory<" << mem->width << "> " << mangle(mem)
-		            << " { " << mem->size << "u";
-		if (!GetSize(mem->inits)) {
-			f << " };\n";
-		} else {
-			f << ",\n";
-			inc_indent();
-				for (auto &init : mem->inits) {
+		int mem_init_idx = 0;
+		inc_indent();
+			for (auto wire : module->wires()) {
+				if (!wire_init.count(wire)) continue;
+
+				f << indent << mangle(wire) << " = ";
+				if (wire_types[wire].is_buffered()) {
+					f << "wire<" << wire->width << ">";
+				} else {
+					f << "value<" << wire->width << ">";
+				}
+				dump_const_init(wire_init.at(wire), wire->width);
+				f << ";\n";
+
+				if (edge_wires[wire] && !wire_types[wire].is_buffered()) {
+					f << indent << "prev_" << mangle(wire) << " = ";
+					dump_const(wire_init.at(wire), wire->width);
+					f << ";\n";
+				}
+			}
+			for (auto &mem : mod_memories[module]) {
+				for (auto &init : mem.inits) {
 					if (init.removed)
 						continue;
 					dump_attrs(&init);
-					int words = GetSize(init.data) / mem->width;
-					f << indent << "memory<" << mem->width << ">::init<" << words << "> { "
-						    << stringf("%#x", init.addr.as_int()) << ", {";
+					int words = GetSize(init.data) / mem.width;
+					f << indent << "static const value<" << mem.width << "> ";
+					f << "mem_init_" << ++mem_init_idx << "[" << words << "] {";
 					inc_indent();
 						for (int n = 0; n < words; n++) {
 							if (n % 4 == 0)
 								f << "\n" << indent;
 							else
 								f << " ";
-							dump_const(init.data, mem->width, n * mem->width, /*fixed_width=*/true);
+							dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true);
 							f << ",";
 						}
 					dec_indent();
-					f << "\n" << indent << "}},\n";
+					f << "\n";
+					f << indent << "};\n";
+					f << indent << "std::copy(std::begin(mem_init_" << mem_init_idx << "), ";
+					f << "std::end(mem_init_" << mem_init_idx << "), ";
+					f << "&" << mangle(&mem) << ".data[" << stringf("%#x", init.addr.as_int()) << "]);\n";
 				}
-			dec_indent();
-			f << indent << "};\n";
-		}
+			}
+			for (auto cell : module->cells()) {
+				if (is_internal_cell(cell->type))
+					continue;
+				f << indent << mangle(cell);
+				RTLIL::Module *cell_module = module->design->module(cell->type);
+				if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
+					f << "->reset();\n";
+				} else {
+					f << ".reset();\n";
+				}
+			}
+		dec_indent();
 	}
 
 	void dump_eval_method(RTLIL::Module *module)
@@ -2200,6 +2212,10 @@ struct CxxrtlWorker {
 						dump_wire(wire, /*is_local=*/false);
 				}
 				f << "\n";
+				f << indent << "void reset() override {\n";
+				dump_reset_method(module);
+				f << indent << "}\n";
+				f << "\n";
 				f << indent << "bool eval() override {\n";
 				dump_eval_method(module);
 				f << indent << "}\n";
@@ -2248,7 +2264,9 @@ struct CxxrtlWorker {
 					dump_debug_wire(wire, /*is_local=*/false);
 				bool has_memories = false;
 				for (auto &mem : mod_memories[module]) {
-					dump_memory(&mem);
+					dump_attrs(&mem);
+					f << indent << "memory<" << mem.width << "> " << mangle(&mem)
+					            << " { " << mem.size << "u };\n";
 					has_memories = true;
 				}
 				if (has_memories)
@@ -2269,52 +2287,20 @@ struct CxxrtlWorker {
 						dump_metadata_map(cell->attributes);
 						f << ");\n";
 					} else {
-						f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n";
+						f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n";
 					}
 					has_cells = true;
 				}
 				if (has_cells)
 					f << "\n";
-				f << indent << mangle(module) << "() {}\n";
-				if (has_cells) {
-					f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) :\n";
-					bool first = true;
-					for (auto cell : module->cells()) {
-						if (is_internal_cell(cell->type))
-							continue;
-						if (first) {
-							first = false;
-						} else {
-							f << ",\n";
-						}
-						RTLIL::Module *cell_module = module->design->module(cell->type);
-						if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
-							f << indent << "  " << mangle(cell) << "(std::move(other." << mangle(cell) << "))";
-						} else {
-							f << indent << "  " << mangle(cell) << "(adopt {}, std::move(other." << mangle(cell) << "))";
-						}
-					}
-					f << " {\n";
-					inc_indent();
-						for (auto cell : module->cells()) {
-							if (is_internal_cell(cell->type))
-								continue;
-							RTLIL::Module *cell_module = module->design->module(cell->type);
-							if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
-								f << indent << mangle(cell) << "->reset();\n";
-						}
-					dec_indent();
-					f << indent << "}\n";
-				} else {
-					f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) {}\n";
-				}
-				f << "\n";
-				f << indent << "void reset() override {\n";
+				f << indent << mangle(module) << "(interior) {}\n";
+				f << indent << mangle(module) << "() {\n";
 				inc_indent();
-					f << indent << "*this = " << mangle(module) << "(adopt {}, std::move(*this));\n";
+					f << indent << "reset();\n";
 				dec_indent();
-				f << indent << "}\n";
+				f << indent << "};\n";
 				f << "\n";
+				f << indent << "void reset() override;\n";
 				f << indent << "bool eval() override;\n";
 				f << indent << "bool commit() override;\n";
 				if (debug_info) {
@@ -2341,6 +2327,10 @@ struct CxxrtlWorker {
 	{
 		if (module->get_bool_attribute(ID(cxxrtl_blackbox)))
 			return;
+		f << indent << "void " << mangle(module) << "::reset() {\n";
+		dump_reset_method(module);
+		f << indent << "}\n";
+		f << "\n";
 		f << indent << "bool " << mangle(module) << "::eval() {\n";
 		dump_eval_method(module);
 		f << indent << "}\n";