mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-30 19:22:31 +00:00 
			
		
		
		
	Generalize muxadd to muxorder
This commit is contained in:
		
							parent
							
								
									2749b6f31b
								
							
						
					
					
						commit
						fa97c4830e
					
				
					 5 changed files with 171 additions and 154 deletions
				
			
		|  | @ -32,7 +32,7 @@ PEEPOPT_PATTERN += passes/opt/peepopt_shiftmul_left.pmg | |||
| PEEPOPT_PATTERN += passes/opt/peepopt_shiftadd.pmg | ||||
| PEEPOPT_PATTERN += passes/opt/peepopt_muldiv.pmg | ||||
| PEEPOPT_PATTERN += passes/opt/peepopt_muldiv_c.pmg | ||||
| PEEPOPT_PATTERN += passes/opt/peepopt_muxadd.pmg | ||||
| PEEPOPT_PATTERN += passes/opt/peepopt_muxorder.pmg | ||||
| PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg | ||||
| 
 | ||||
| passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | ||||
|  |  | |||
|  | @ -68,9 +68,11 @@ struct PeepoptPass : public Pass { | |||
| 		log("                   based pattern to prevent combinational paths from the\n"); | ||||
| 		log("                   output to the enable input after running clk2fflogic.\n"); | ||||
| 		log("\n"); | ||||
| 		log("If -withmuxadd is specified it adds the following rule:\n"); | ||||
| 		log("If -muxorder is specified it adds the following rule:\n"); | ||||
| 		log("\n"); | ||||
| 		log("   * muxadd - Replace S?(A+B):A with A+(S?B:0)\n"); | ||||
| 		log("   * muxorder - Replace S?(A OP B):A with A OP (S?B:I) where I is identity of OP\n"); | ||||
| 		log("                Ex 1:   S?(A + B):A   --->   A + (S?B:0)\n"); | ||||
| 		log("                Ex 2:   S?(A * B):A   --->   A & (S?B:1)\n"); | ||||
| 		log("\n"); | ||||
| 	} | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) override | ||||
|  | @ -78,7 +80,7 @@ struct PeepoptPass : public Pass { | |||
| 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | ||||
| 
 | ||||
| 		bool formalclk = false; | ||||
| 		bool withmuxadd = false; | ||||
| 		bool muxorder = false; | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | ||||
| 		{ | ||||
|  | @ -86,8 +88,8 @@ struct PeepoptPass : public Pass { | |||
| 				formalclk = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-withmuxadd") { | ||||
| 				withmuxadd = true; | ||||
| 			if (args[argidx] == "-muxorder") { | ||||
| 				muxorder = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
|  | @ -119,8 +121,8 @@ struct PeepoptPass : public Pass { | |||
| 					pm.run_shiftmul_left(); | ||||
| 					pm.run_muldiv(); | ||||
| 					pm.run_muldiv_c(); | ||||
| 					if (withmuxadd) | ||||
| 						pm.run_muxadd(); | ||||
| 					if (muxorder) | ||||
| 						pm.run_muxorder(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,128 +0,0 @@ | |||
| pattern muxadd | ||||
| // | ||||
| // Authored by Akash Levy and Alain Dargelas of Silimate, Inc. under ISC license. | ||||
| // Transforms add->mux into mux->add: | ||||
| // y = s ? (a + b) : a   ===>   y = a + (s ? b : 0) | ||||
| // or | ||||
| // y = s ? a : (a + b)   ===>   y = a + (s ? 0 : b) | ||||
| 
 | ||||
| state <SigSpec> add_a add_b add_y add_a_ext mux_a mux_b mux_y | ||||
| state <Const> add_a_signed | ||||
| state <IdString> add_a_id add_b_id mux_a_id mux_b_id | ||||
| 
 | ||||
| match add | ||||
| 	// Select adder | ||||
| 	select add->type == $add | ||||
| 
 | ||||
| 	// Set ports, allowing A and B to be swapped | ||||
| 	choice <IdString> A {\A, \B} | ||||
| 	define <IdString> B (A == \A ? \B : \A) | ||||
| 	set add_a port(add, A) | ||||
| 	set add_b port(add, B) | ||||
| 	set add_y port(add, \Y) | ||||
| 
 | ||||
| 	// Get signedness | ||||
| 	set add_a_signed param(add, (A == \A) ? \A_SIGNED : \B_SIGNED) | ||||
| 
 | ||||
| 	// Choice ids | ||||
| 	set add_a_id A | ||||
| 	set add_b_id B | ||||
| endmatch | ||||
| 
 | ||||
| code add_y add_a add_b add_a_ext | ||||
| 	// Get adder signals | ||||
| 	add_a_ext = SigSpec(port(add, add_a_id)); | ||||
| 	add_a_ext.extend_u0(GetSize(add_y), add_a_signed.as_bool()); | ||||
| 
 | ||||
| 	// Fanout of each adder Y bit should be 1 (no bit-split) | ||||
| 	if (nusers(add_y) != 2) | ||||
| 		reject; | ||||
| endcode | ||||
| 
 | ||||
| match mux  | ||||
| 	// Select mux of form: s ? (a + b) : a | ||||
| 	// Allow leading 0s when A_WIDTH != Y_WIDTH or s ? a : (a + b) | ||||
| 	select mux->type == $mux | ||||
| 	choice <IdString> AB {\A, \B} | ||||
| 	define <IdString> BA (AB == \A ? \B : \A) | ||||
| 	set mux_y port(mux, \Y) | ||||
| 	set mux_a port(mux, AB) | ||||
| 	set mux_b port(mux, BA) | ||||
| 	set mux_a_id AB | ||||
| 	set mux_b_id BA | ||||
| 	index <SigSpec> port(mux, AB) === add_a_ext | ||||
| 	index <SigSpec> port(mux, BA) === add_y | ||||
| endmatch | ||||
| 
 | ||||
| code add_y add_a add_b add_a_ext add_a_id add_b_id  mux_y mux_a mux_b mux_a_id mux_b_id | ||||
| 	// Get mux signal | ||||
| 	SigSpec mid;  | ||||
| 	std::string adder_y_name; | ||||
| 	if (add_y.is_wire())  | ||||
| 		adder_y_name = add_y.as_wire()->name.c_str(); | ||||
| 	else | ||||
| 		adder_y_name = add_y.as_string(); | ||||
| 
 | ||||
| 	// Start by renaming the LHS of an eventual assign statement | ||||
| 	// where the RHS is the adder output (that is getting rewired). | ||||
| 	// Renaming the signal allows equiv_opt to function as it would | ||||
| 	// otherwise try to match the functionality which would fail | ||||
| 	// as the LHS signal has indeed changed function. | ||||
| 
 | ||||
| 	// Adder output could be assigned | ||||
| 	for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { | ||||
| 		RTLIL::SigSpec rhs = it->second; | ||||
| 		if (rhs.is_wire()) { | ||||
| 			const std::string& rhs_name = rhs.as_wire()->name.c_str(); | ||||
| 			if (rhs_name == adder_y_name) { | ||||
| 				RTLIL::SigSpec lhs = it->first; | ||||
| 				if (lhs.is_wire()) { | ||||
| 					const std::string& lhs_name =  lhs.as_wire()->name.c_str(); | ||||
| 					module->rename(lhs_name, module->uniquify("$" + lhs_name)); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Alternatively, the port name could be a wire name | ||||
| 	if (add_y.is_wire()) { | ||||
| 		if (GetSize(adder_y_name)) { | ||||
| 			if (adder_y_name[0] != '$') { | ||||
| 				module->rename(adder_y_name, module->uniquify("$" + adder_y_name)); | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		for (auto chunk : add_y.chunks()) { | ||||
| 			if (chunk.is_wire()) { | ||||
| 				const std::string& name = chunk.wire->name.c_str(); | ||||
| 				if (name[0] != '$') { | ||||
| 					module->rename(name, module->uniquify("$" + name)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Create new mid wire | ||||
| 	Cell *cell = mux; | ||||
| 	mid = module->addWire(NEW_ID2_SUFFIX("mid"), GetSize(add_b)); | ||||
| 
 | ||||
| 	// Connect ports | ||||
| 	add->setPort(add_b_id, mid); | ||||
| 	add->setPort(add_a_id, add_a); | ||||
| 	add->setPort(\Y, add_y); | ||||
| 	cell = add; | ||||
| 	module->rename(add, NEW_ID2_SUFFIX("rot")); | ||||
| 	mux->setPort(mux_a_id, Const(State::S0, GetSize(add_b))); | ||||
| 	mux->setPort(mux_b_id, add_b); | ||||
| 	mux->setPort(\Y, mid); | ||||
| 	cell = mux; | ||||
| 	module->rename(mux, NEW_ID2_SUFFIX("rot")); | ||||
| 	module->connect(mux_y, add_y); | ||||
| 
 | ||||
| 	// Log, fixup, accept | ||||
| 	log("muxadd pattern in %s: mux=%s, add=%s\n", log_id(module), log_id(mux), log_id(add)); | ||||
| 	add->fixup_parameters(); | ||||
| 	mux->fixup_parameters(); | ||||
| 	did_something = true; | ||||
| 	accept; | ||||
| endcode | ||||
							
								
								
									
										143
									
								
								passes/opt/peepopt_muxorder.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								passes/opt/peepopt_muxorder.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,143 @@ | |||
| pattern muxorder | ||||
| // | ||||
| // Authored by Akash Levy and Alain Dargelas of Silimate, Inc. under ISC license. | ||||
| // | ||||
| // Transforms OP->mux into mux->OP using identity input for two-input OP. Example: | ||||
| // y = s ? (a + b) : a   ===>   y = a + (s ? b : 0) | ||||
| // or | ||||
| // y = s ? a : (a + b)   ===>   y = a + (s ? 0 : b) | ||||
| // | ||||
| // Supported OPs: +, *, &, |, ^, ^~ | ||||
| // | ||||
| 
 | ||||
| state <SigSpec> op_a op_b op_y op_a_ext mux_a mux_b mux_y | ||||
| state <Const> op_a_signed | ||||
| state <IdString> op_a_id op_b_id mux_a_id mux_b_id | ||||
| 
 | ||||
| match op | ||||
| 	// Select OP | ||||
| 	select op->type.in($add, $mul, $and, $or, $xor, $xnor) | ||||
| 
 | ||||
| 	// Set ports, allowing A and B to be swapped | ||||
| 	choice <IdString> A {\A, \B} | ||||
| 	define <IdString> B (A == \A ? \B : \A) | ||||
| 	set op_a port(op, A) | ||||
| 	set op_b port(op, B) | ||||
| 	set op_y port(op, \Y) | ||||
| 
 | ||||
| 	// Get signedness | ||||
| 	set op_a_signed param(op, (A == \A) ? \A_SIGNED : \B_SIGNED) | ||||
| 
 | ||||
| 	// Choice ids | ||||
| 	set op_a_id A | ||||
| 	set op_b_id B | ||||
| endmatch | ||||
| 
 | ||||
| code op_y op_a op_b op_a_ext | ||||
| 	// Get OP signals | ||||
| 	op_a_ext = SigSpec(port(op, op_a_id)); | ||||
| 	op_a_ext.extend_u0(GetSize(op_y), op_a_signed.as_bool()); | ||||
| 
 | ||||
| 	// Fanout of each OP Y bit should be 1 (no bit-split) | ||||
| 	if (nusers(op_y) != 2) | ||||
| 		reject; | ||||
| endcode | ||||
| 
 | ||||
| match mux  | ||||
| 	// Select mux of form: s ? (a + b) : a | ||||
| 	// Allow leading 0s when A_WIDTH != Y_WIDTH or s ? a : (a + b) | ||||
| 	select mux->type == $mux | ||||
| 	choice <IdString> AB {\A, \B} | ||||
| 	define <IdString> BA (AB == \A ? \B : \A) | ||||
| 	set mux_y port(mux, \Y) | ||||
| 	set mux_a port(mux, AB) | ||||
| 	set mux_b port(mux, BA) | ||||
| 	set mux_a_id AB | ||||
| 	set mux_b_id BA | ||||
| 	index <SigSpec> port(mux, AB) === op_a_ext | ||||
| 	index <SigSpec> port(mux, BA) === op_y | ||||
| endmatch | ||||
| 
 | ||||
| code op_y op_a op_b op_a_ext op_a_id op_b_id  mux_y mux_a mux_b mux_a_id mux_b_id | ||||
| 	// Get mux signal | ||||
| 	SigSpec mid;  | ||||
| 	std::string op_y_name; | ||||
| 	if (op_y.is_wire())  | ||||
| 		op_y_name = op_y.as_wire()->name.c_str(); | ||||
| 	else | ||||
| 		op_y_name = op_y.as_string(); | ||||
| 
 | ||||
| 	// Start by renaming the LHS of an eventual assign statement | ||||
| 	// where the RHS is the OP output (that is getting rewired). | ||||
| 	// Renaming the signal allows equiv_opt to function as it would | ||||
| 	// otherwise try to match the functionality which would fail | ||||
| 	// as the LHS signal has indeed changed function. | ||||
| 
 | ||||
| 	// OP output could be assigned | ||||
| 	for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { | ||||
| 		RTLIL::SigSpec rhs = it->second; | ||||
| 		if (rhs.is_wire()) { | ||||
| 			const std::string& rhs_name = rhs.as_wire()->name.c_str(); | ||||
| 			if (rhs_name == op_y_name) { | ||||
| 				RTLIL::SigSpec lhs = it->first; | ||||
| 				if (lhs.is_wire()) { | ||||
| 					const std::string& lhs_name =  lhs.as_wire()->name.c_str(); | ||||
| 					module->rename(lhs_name, module->uniquify("$" + lhs_name)); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Alternatively, the port name could be a wire name | ||||
| 	if (op_y.is_wire()) { | ||||
| 		if (GetSize(op_y_name)) { | ||||
| 			if (op_y_name[0] != '$') { | ||||
| 				module->rename(op_y_name, module->uniquify("$" + op_y_name)); | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		for (auto chunk : op_y.chunks()) { | ||||
| 			if (chunk.is_wire()) { | ||||
| 				const std::string& name = chunk.wire->name.c_str(); | ||||
| 				if (name[0] != '$') { | ||||
| 					module->rename(name, module->uniquify("$" + name)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Create new mid wire | ||||
| 	Cell *cell = mux; | ||||
| 	mid = module->addWire(NEW_ID2_SUFFIX("mid"), GetSize(op_b)); | ||||
| 
 | ||||
| 	// Determine identity input of operator | ||||
| 	Const identity; | ||||
| 	if (op->type.in($add, $or, $xor)) | ||||
| 	  identity = Const(0, GetSize(op_b)); | ||||
| 	else if (op->type == $mul) | ||||
| 		identity = Const(1, GetSize(op_b)); | ||||
| 	else if (op->type.in($and, $xnor)) | ||||
| 	  identity = Const(State::S1, GetSize(op_b)); | ||||
| 	else | ||||
| 		log_assert(0); // invalid operator, should never happen | ||||
| 
 | ||||
| 	// Connect ports | ||||
| 	op->setPort(op_b_id, mid); | ||||
| 	op->setPort(op_a_id, op_a); | ||||
| 	op->setPort(\Y, op_y); | ||||
| 	cell = op; | ||||
| 	module->rename(op, NEW_ID2_SUFFIX("reord")); | ||||
| 	mux->setPort(mux_a_id, identity); | ||||
| 	mux->setPort(mux_b_id, op_b); | ||||
| 	mux->setPort(\Y, mid); | ||||
| 	cell = mux; | ||||
| 	module->rename(mux, NEW_ID2_SUFFIX("reord")); | ||||
| 	module->connect(mux_y, op_y); | ||||
| 
 | ||||
| 	// Log, fixup, accept | ||||
| 	log("muxorder pattern in %s: mux=%s, op=%s, optype=%s\n", log_id(module), log_id(mux), log_id(op), log_id(op->type)); | ||||
| 	op->fixup_parameters(); | ||||
| 	mux->fixup_parameters(); | ||||
| 	did_something = true; | ||||
| 	accept; | ||||
| endcode | ||||
|  | @ -18,7 +18,7 @@ module top(a, b, s, y); | |||
| endmodule | ||||
| EOF | ||||
| check -assert | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| log -pop | ||||
|  | @ -36,7 +36,7 @@ module top(a, b, s, y); | |||
| endmodule | ||||
| EOF | ||||
| check -assert | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| log -pop | ||||
|  | @ -56,7 +56,7 @@ module top(a, b, s, y); | |||
| endmodule | ||||
| EOF | ||||
| check -assert | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| log -pop | ||||
|  | @ -77,7 +77,7 @@ module top(a_, b, s, y); | |||
| endmodule | ||||
| EOF | ||||
| check -assert | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| log -pop | ||||
|  | @ -101,7 +101,7 @@ module top(a, b_, f, s, y_); | |||
| endmodule | ||||
| EOF | ||||
| check -assert | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| log -pop | ||||
|  | @ -121,7 +121,7 @@ module top(a, b, ab, s, y); | |||
| endmodule | ||||
| EOF | ||||
| check -assert | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-none t:$add %co1 %a w:y %i | ||||
| log -pop | ||||
|  | @ -141,7 +141,7 @@ module top(a, b, s, y, z); | |||
| endmodule | ||||
| EOF | ||||
| check -assert | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-none t:$add %co1 %a w:y %i | ||||
| 
 | ||||
|  | @ -162,7 +162,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-none t:$add %co1 %a w:y %i | ||||
| 
 | ||||
|  | @ -183,7 +183,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-none t:$add %co1 %a w:y %i | ||||
| 
 | ||||
|  | @ -204,7 +204,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-none t:$add %co1 %a w:y %i | ||||
| 
 | ||||
|  | @ -226,7 +226,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-none t:$add %co1 %a w:y %i | ||||
| 
 | ||||
|  | @ -247,7 +247,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i | ||||
| 
 | ||||
|  | @ -268,7 +268,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i | ||||
| 
 | ||||
|  | @ -289,7 +289,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| 
 | ||||
|  | @ -310,7 +310,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| 
 | ||||
|  | @ -331,7 +331,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| 
 | ||||
|  | @ -352,7 +352,7 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
| 
 | ||||
|  | @ -373,6 +373,6 @@ EOF | |||
| check -assert | ||||
| wreduce | ||||
| opt_clean | ||||
| equiv_opt -assert peepopt -withmuxadd | ||||
| equiv_opt -assert peepopt -muxorder | ||||
| design -load postopt | ||||
| select -assert-any t:$add %co1 %a w:y %i # assert adder rewired | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue