mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	document functionalir.h and change visitors to derive from AbstractVisitor. remove extraneous widths arguments from visitors.
This commit is contained in:
		
							parent
							
								
									6922633b0b
								
							
						
					
					
						commit
						55c2c17853
					
				
					 5 changed files with 400 additions and 170 deletions
				
			
		| 
						 | 
				
			
			@ -109,7 +109,7 @@ public:
 | 
			
		|||
        {
 | 
			
		||||
            auto found = graph_->sparse_attrs.find(index_);
 | 
			
		||||
            log_assert(found != graph_->sparse_attrs.end());
 | 
			
		||||
            return *found;
 | 
			
		||||
            return found->second;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,83 @@
 | 
			
		|||
 | 
			
		||||
YOSYS_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
const char *FunctionalIR::fn_to_string(FunctionalIR::Fn fn) {
 | 
			
		||||
	switch(fn) {
 | 
			
		||||
	case FunctionalIR::Fn::invalid: return "invalid";
 | 
			
		||||
	case FunctionalIR::Fn::buf: return "buf";
 | 
			
		||||
	case FunctionalIR::Fn::slice: return "slice";
 | 
			
		||||
	case FunctionalIR::Fn::zero_extend: return "zero_extend";
 | 
			
		||||
	case FunctionalIR::Fn::sign_extend: return "sign_extend";
 | 
			
		||||
	case FunctionalIR::Fn::concat: return "concat";
 | 
			
		||||
	case FunctionalIR::Fn::add: return "add";
 | 
			
		||||
	case FunctionalIR::Fn::sub: return "sub";
 | 
			
		||||
	case FunctionalIR::Fn::mul: return "mul";
 | 
			
		||||
	case FunctionalIR::Fn::unsigned_div: return "unsigned_div";
 | 
			
		||||
	case FunctionalIR::Fn::unsigned_mod: return "unsigned_mod";
 | 
			
		||||
	case FunctionalIR::Fn::bitwise_and: return "bitwise_and";
 | 
			
		||||
	case FunctionalIR::Fn::bitwise_or: return "bitwise_or";
 | 
			
		||||
	case FunctionalIR::Fn::bitwise_xor: return "bitwise_xor";
 | 
			
		||||
	case FunctionalIR::Fn::bitwise_not: return "bitwise_not";
 | 
			
		||||
	case FunctionalIR::Fn::reduce_and: return "reduce_and";
 | 
			
		||||
	case FunctionalIR::Fn::reduce_or: return "reduce_or";
 | 
			
		||||
	case FunctionalIR::Fn::reduce_xor: return "reduce_xor";
 | 
			
		||||
	case FunctionalIR::Fn::unary_minus: return "unary_minus";
 | 
			
		||||
	case FunctionalIR::Fn::equal: return "equal";
 | 
			
		||||
	case FunctionalIR::Fn::not_equal: return "not_equal";
 | 
			
		||||
	case FunctionalIR::Fn::signed_greater_than: return "signed_greater_than";
 | 
			
		||||
	case FunctionalIR::Fn::signed_greater_equal: return "signed_greater_equal";
 | 
			
		||||
	case FunctionalIR::Fn::unsigned_greater_than: return "unsigned_greater_than";
 | 
			
		||||
	case FunctionalIR::Fn::unsigned_greater_equal: return "unsigned_greater_equal";
 | 
			
		||||
	case FunctionalIR::Fn::logical_shift_left: return "logical_shift_left";
 | 
			
		||||
	case FunctionalIR::Fn::logical_shift_right: return "logical_shift_right";
 | 
			
		||||
	case FunctionalIR::Fn::arithmetic_shift_right: return "arithmetic_shift_right";
 | 
			
		||||
	case FunctionalIR::Fn::mux: return "mux";
 | 
			
		||||
	case FunctionalIR::Fn::pmux: return "pmux";
 | 
			
		||||
	case FunctionalIR::Fn::constant: return "constant";
 | 
			
		||||
	case FunctionalIR::Fn::input: return "input";
 | 
			
		||||
	case FunctionalIR::Fn::state: return "state";
 | 
			
		||||
	case FunctionalIR::Fn::multiple: return "multiple";
 | 
			
		||||
	case FunctionalIR::Fn::undriven: return "undriven";
 | 
			
		||||
	case FunctionalIR::Fn::memory_read: return "memory_read";
 | 
			
		||||
	case FunctionalIR::Fn::memory_write: return "memory_write";
 | 
			
		||||
	}
 | 
			
		||||
	log_error("fn_to_string: unknown FunctionalIR::Fn value %d", (int)fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct PrintVisitor : FunctionalIR::DefaultVisitor<std::string> {
 | 
			
		||||
	using Node = FunctionalIR::Node;
 | 
			
		||||
	std::function<std::string(Node)> np;
 | 
			
		||||
	PrintVisitor(std::function<std::string(Node)> np) : np(np) { }
 | 
			
		||||
	// as a general rule the default handler is good enough iff the only arguments are of type Node
 | 
			
		||||
	std::string slice(Node, Node a, int offset, int out_width) override { return "slice(" + np(a) + ", " + std::to_string(offset) + ", " + std::to_string(out_width) + ")"; }
 | 
			
		||||
	std::string zero_extend(Node, Node a, int out_width) override { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
 | 
			
		||||
	std::string sign_extend(Node, Node a, int out_width) override { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
 | 
			
		||||
	std::string constant(Node, RTLIL::Const value) override { return "constant(" + value.as_string() + ")"; }
 | 
			
		||||
	std::string input(Node, IdString name) override { return "input(" + name.str() + ")"; }
 | 
			
		||||
	std::string state(Node, IdString name) override { return "state(" + name.str() + ")"; }
 | 
			
		||||
	std::string undriven(Node, int width) override { return "undriven(" + std::to_string(width) + ")"; }
 | 
			
		||||
	std::string default_handler(Node self) override {
 | 
			
		||||
		std::string ret = FunctionalIR::fn_to_string(self.fn());
 | 
			
		||||
		ret += "(";
 | 
			
		||||
		for(size_t i = 0; i < self.arg_count(); i++) {
 | 
			
		||||
			if(i > 0) ret += ", ";
 | 
			
		||||
			ret += np(self.arg(i));
 | 
			
		||||
		}
 | 
			
		||||
		ret += ")";
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::string FunctionalIR::Node::to_string()
 | 
			
		||||
{
 | 
			
		||||
	return to_string([](Node n) { return RTLIL::unescape_id(n.name()); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string FunctionalIR::Node::to_string(std::function<std::string(Node)> np)
 | 
			
		||||
{
 | 
			
		||||
	return visit(PrintVisitor(np));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class T, class Factory>
 | 
			
		||||
class CellSimplifier {
 | 
			
		||||
	Factory &factory;
 | 
			
		||||
| 
						 | 
				
			
			@ -47,11 +124,6 @@ class CellSimplifier {
 | 
			
		|||
		return neg_if(a, a_width, sign(a, a_width));
 | 
			
		||||
	}
 | 
			
		||||
public:
 | 
			
		||||
	T reduce_or(T a, int width) {
 | 
			
		||||
		if (width == 1)
 | 
			
		||||
			return a;
 | 
			
		||||
		return factory.reduce_or(a, width);
 | 
			
		||||
	}
 | 
			
		||||
	T extend(T a, int in_width, int out_width, bool is_signed) {
 | 
			
		||||
		if(in_width == out_width)
 | 
			
		||||
			return a;
 | 
			
		||||
| 
						 | 
				
			
			@ -153,8 +225,8 @@ public:
 | 
			
		|||
			else
 | 
			
		||||
				log_abort();
 | 
			
		||||
		}else if(cellType.in({ID($logic_or), ID($logic_and)})){
 | 
			
		||||
			T a = reduce_or(inputs.at(ID(A)), a_width);
 | 
			
		||||
			T b = reduce_or(inputs.at(ID(B)), b_width);
 | 
			
		||||
			T a = factory.reduce_or(inputs.at(ID(A)), a_width);
 | 
			
		||||
			T b = factory.reduce_or(inputs.at(ID(B)), b_width);
 | 
			
		||||
			T y = cellType == ID($logic_and) ? factory.bitwise_and(a, b, 1) : factory.bitwise_or(a, b, 1);
 | 
			
		||||
			return extend(y, 1, y_width, false);
 | 
			
		||||
		}else if(cellType == ID($not)){
 | 
			
		||||
| 
						 | 
				
			
			@ -166,11 +238,11 @@ public:
 | 
			
		|||
			T a = extend(inputs.at(ID(A)), a_width, y_width, a_signed);
 | 
			
		||||
			return factory.unary_minus(a, y_width);
 | 
			
		||||
		}else if(cellType == ID($logic_not)){
 | 
			
		||||
			T a = reduce_or(inputs.at(ID(A)), a_width);
 | 
			
		||||
			T a = factory.reduce_or(inputs.at(ID(A)), a_width);
 | 
			
		||||
			T y = factory.bitwise_not(a, 1);
 | 
			
		||||
			return extend(y, 1, y_width, false);
 | 
			
		||||
		}else if(cellType.in({ID($reduce_or), ID($reduce_bool)})){
 | 
			
		||||
			T a = reduce_or(inputs.at(ID(A)), a_width);
 | 
			
		||||
			T a = factory.reduce_or(inputs.at(ID(A)), a_width);
 | 
			
		||||
			return extend(a, 1, y_width, false);
 | 
			
		||||
		}else if(cellType == ID($reduce_and)){
 | 
			
		||||
			T a = factory.reduce_and(inputs.at(ID(A)), a_width);
 | 
			
		||||
| 
						 | 
				
			
			@ -244,7 +316,7 @@ public:
 | 
			
		|||
					// which equals the negative of (-a) / b with rounding up rather than down
 | 
			
		||||
					// note that to handle the case where a = most negative value properly,
 | 
			
		||||
					// we have to calculate a1_sign from the original values rather than using sign(a1, width)
 | 
			
		||||
					T a1_sign = factory.bitwise_and(factory.not_equal(sign(a, width), sign(b, width), 1), reduce_or(a, width), 1);
 | 
			
		||||
					T a1_sign = factory.bitwise_and(factory.not_equal(sign(a, width), sign(b, width), 1), factory.reduce_or(a, width), 1);
 | 
			
		||||
					T a2 = factory.mux(a1, factory.bitwise_not(a1, width), a1_sign, width);
 | 
			
		||||
					T y1 = factory.unsigned_div(a2, b1, width);
 | 
			
		||||
					T y2 = extend(y1, width, y_width, false);
 | 
			
		||||
| 
						 | 
				
			
			@ -560,6 +632,7 @@ void FunctionalIR::forward_buf() {
 | 
			
		|||
    _graph.permute(perm, alias);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Quoting routine to make error messages nicer
 | 
			
		||||
static std::string quote_fmt(const char *fmt)
 | 
			
		||||
{
 | 
			
		||||
	std::string r;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,45 +31,123 @@ YOSYS_NAMESPACE_BEGIN
 | 
			
		|||
 | 
			
		||||
class FunctionalIR {
 | 
			
		||||
public:
 | 
			
		||||
	// each function is documented with a short pseudocode declaration or definition
 | 
			
		||||
	// standard C/Verilog operators are used to describe the result
 | 
			
		||||
	// 
 | 
			
		||||
	// the types used in this are:
 | 
			
		||||
	// - bit[N]: a bitvector of N bits
 | 
			
		||||
	//   bit[N] can be indicated as signed or unsigned. this is not tracked by the functional backend
 | 
			
		||||
	//   but is meant to indicate how the value is interpreted
 | 
			
		||||
	//   if a bit[N] is marked as neither signed nor unsigned, this means the result should be valid with *either* interpretation
 | 
			
		||||
	// - memory[N, M]: a memory with N address and M data bits
 | 
			
		||||
	// - int: C++ int
 | 
			
		||||
	// - Const[N]: yosys RTLIL::Const (with size() == N)
 | 
			
		||||
	// - IdString: yosys IdString
 | 
			
		||||
	// - any: used in documentation to indicate that the type is unconstrained
 | 
			
		||||
	//
 | 
			
		||||
	// nodes in the functional backend are either of type bit[N] or memory[N,M] (for some N, M: int)
 | 
			
		||||
	// additionally, they can carry a constant of type int, Const[N] or IdString
 | 
			
		||||
	// each node has a 'sort' field that stores the type of the node
 | 
			
		||||
	// slice, zero_extend, sign_extend use the type field to store out_width
 | 
			
		||||
	enum class Fn {
 | 
			
		||||
		// invalid() = known-invalid/shouldn't happen value
 | 
			
		||||
		// TODO: maybe remove this and use e.g. std::optional instead?
 | 
			
		||||
		invalid,
 | 
			
		||||
		// buf(a: any): any = a
 | 
			
		||||
		// no-op operation
 | 
			
		||||
		// when constructing the compute graph we generate invalid buf() nodes as a placeholder
 | 
			
		||||
		// and later insert the argument
 | 
			
		||||
		buf,
 | 
			
		||||
		// slice(a: bit[in_width], offset: int, out_width: int): bit[out_width] = a[offset +: out_width]
 | 
			
		||||
		// required: offset + out_width <= in_width
 | 
			
		||||
		slice,
 | 
			
		||||
		// zero_extend(a: unsigned bit[in_width], out_width: int): unsigned bit[out_width] = a (zero extended)
 | 
			
		||||
		// required: out_width > in_width
 | 
			
		||||
		zero_extend,
 | 
			
		||||
		// sign_extend(a: signed bit[in_width], out_width: int): signed bit[out_width] = a (sign extended)
 | 
			
		||||
		// required: out_width > in_width
 | 
			
		||||
		sign_extend,
 | 
			
		||||
		// concat(a: bit[N], b: bit[M]): bit[N+M] = {b, a} (verilog syntax)
 | 
			
		||||
		// concatenates two bitvectors, with a in the least significant position and b in the more significant position
 | 
			
		||||
		concat,
 | 
			
		||||
		// add(a: bit[N], b: bit[N]): bit[N] = a + b
 | 
			
		||||
		add,
 | 
			
		||||
		// sub(a: bit[N], b: bit[N]): bit[N] = a - b
 | 
			
		||||
		sub,
 | 
			
		||||
		// mul(a: bit[N], b: bit[N]): bit[N] = a * b
 | 
			
		||||
		mul,
 | 
			
		||||
		// unsigned_div(a: unsigned bit[N], b: unsigned bit[N]): bit[N] = a / b
 | 
			
		||||
		unsigned_div,
 | 
			
		||||
		// unsigned_mod(a: signed bit[N], b: signed bit[N]): bit[N] = a % b
 | 
			
		||||
		unsigned_mod,
 | 
			
		||||
		// bitwise_and(a: bit[N], b: bit[N]): bit[N] = a & b
 | 
			
		||||
		bitwise_and,
 | 
			
		||||
		// bitwise_or(a: bit[N], b: bit[N]): bit[N] = a | b
 | 
			
		||||
		bitwise_or,
 | 
			
		||||
		// bitwise_xor(a: bit[N], b: bit[N]): bit[N] = a ^ b
 | 
			
		||||
		bitwise_xor,
 | 
			
		||||
		// bitwise_not(a: bit[N]): bit[N] = ~a
 | 
			
		||||
		bitwise_not,
 | 
			
		||||
		// reduce_and(a: bit[N]): bit[1] = &a
 | 
			
		||||
		reduce_and,
 | 
			
		||||
		// reduce_or(a: bit[N]): bit[1] = |a
 | 
			
		||||
		reduce_or,
 | 
			
		||||
		// reduce_xor(a: bit[N]): bit[1] = ^a
 | 
			
		||||
		reduce_xor,
 | 
			
		||||
		// unary_minus(a: bit[N]): bit[N] = -a
 | 
			
		||||
		unary_minus,
 | 
			
		||||
		// equal(a: bit[N], b: bit[N]): bit[1] = (a == b)
 | 
			
		||||
		equal,
 | 
			
		||||
		// not_equal(a: bit[N], b: bit[N]): bit[1] = (a != b)
 | 
			
		||||
		not_equal,
 | 
			
		||||
		// signed_greater_than(a: signed bit[N], b: signed bit[N]): bit[1] = (a > b)
 | 
			
		||||
		signed_greater_than,
 | 
			
		||||
		// signed_greater_equal(a: signed bit[N], b: signed bit[N]): bit[1] = (a >= b)
 | 
			
		||||
		signed_greater_equal,
 | 
			
		||||
		// unsigned_greater_than(a: unsigned bit[N], b: unsigned bit[N]): bit[1] = (a > b)
 | 
			
		||||
		unsigned_greater_than,
 | 
			
		||||
		// unsigned_greater_equal(a: unsigned bit[N], b: unsigned bit[N]): bit[1] = (a >= b)
 | 
			
		||||
		unsigned_greater_equal,
 | 
			
		||||
		// logical_shift_left(a: bit[N], b: unsigned bit[M]): bit[N] = a << b
 | 
			
		||||
		// required: M <= clog2(N + 1)
 | 
			
		||||
		logical_shift_left,
 | 
			
		||||
		// logical_shift_right(a: unsigned bit[N], b: unsigned bit[M]): unsigned bit[N] = a >> b
 | 
			
		||||
		// required: M <= clog2(N + 1)
 | 
			
		||||
		logical_shift_right,
 | 
			
		||||
		// arithmetic_shift_right(a: signed bit[N], b: unsigned bit[M]): signed bit[N] = a >> b
 | 
			
		||||
		// required: M <= clog2(N + 1)
 | 
			
		||||
		arithmetic_shift_right,
 | 
			
		||||
		// mux(a: bit[N], b: bit[N], s: bit[1]): bit[N] = s ? b : a
 | 
			
		||||
		mux,
 | 
			
		||||
		// pmux(a: bit[N], b: bit[N*M], s: bit[M]): bit[N]
 | 
			
		||||
		// required: no more than one bit in b is set
 | 
			
		||||
		// if s[i] = 1 for any i, then returns b[i * N +: N]
 | 
			
		||||
		// returns a if s == 0
 | 
			
		||||
		pmux,
 | 
			
		||||
		// constant(a: Const[N]): bit[N] = a
 | 
			
		||||
		constant,
 | 
			
		||||
		// input(a: IdString): any
 | 
			
		||||
		// returns the current value of the input with the specified name
 | 
			
		||||
		input,
 | 
			
		||||
		// state(a: IdString): any
 | 
			
		||||
		// returns the current value of the state variable with the specified name
 | 
			
		||||
		state,
 | 
			
		||||
		// multiple(a: any, b: any, c: any, ...): any
 | 
			
		||||
		// indicates a value driven by multiple inputs
 | 
			
		||||
		multiple,
 | 
			
		||||
		// undriven(width: int): bit[width]
 | 
			
		||||
		// indicates an undriven value
 | 
			
		||||
		undriven,
 | 
			
		||||
		// memory_read(memory: memory[addr_width, data_width], addr: bit[addr_width]): bit[data_width] = memory[addr]
 | 
			
		||||
		memory_read,
 | 
			
		||||
		// memory_write(memory: memory[addr_width, data_width], addr: bit[addr_width], data: bit[data_width]): memory[addr_width, data_width]
 | 
			
		||||
		// returns a copy of `memory` but with the value at `addr` changed to `data`
 | 
			
		||||
		memory_write
 | 
			
		||||
	};
 | 
			
		||||
	// returns the name of a FunctionalIR::Fn value, as a string literal
 | 
			
		||||
	static const char *fn_to_string(Fn);
 | 
			
		||||
	// FunctionalIR::Sort represents the sort or type of a node
 | 
			
		||||
	// currently the only two types are signal/bit and memory
 | 
			
		||||
	class Sort {
 | 
			
		||||
		std::variant<int, std::pair<int, int>> _v;
 | 
			
		||||
	public:
 | 
			
		||||
| 
						 | 
				
			
			@ -77,13 +155,18 @@ public:
 | 
			
		|||
		Sort(int addr_width, int data_width) : _v(std::make_pair(addr_width, data_width)) { }
 | 
			
		||||
		bool is_signal() const { return _v.index() == 0; }
 | 
			
		||||
		bool is_memory() const { return _v.index() == 1; }
 | 
			
		||||
		// returns the width of a bitvector type, errors out for other types
 | 
			
		||||
		int width() const { return std::get<0>(_v); }
 | 
			
		||||
		// returns the address width of a bitvector type, errors out for other types
 | 
			
		||||
		int addr_width() const { return std::get<1>(_v).first; }
 | 
			
		||||
		// returns the data width of a bitvector type, errors out for other types
 | 
			
		||||
		int data_width() const { return std::get<1>(_v).second; }
 | 
			
		||||
		bool operator==(Sort const& other) const { return _v == other._v; }
 | 
			
		||||
		unsigned int hash() const { return mkhash(_v); }
 | 
			
		||||
	};
 | 
			
		||||
private:
 | 
			
		||||
	// one NodeData is stored per Node, containing the function and non-node arguments
 | 
			
		||||
	// note that NodeData is deduplicated by ComputeGraph
 | 
			
		||||
	class NodeData {
 | 
			
		||||
		Fn _fn;
 | 
			
		||||
		std::variant<
 | 
			
		||||
| 
						 | 
				
			
			@ -107,9 +190,14 @@ private:
 | 
			
		|||
			return _fn == other._fn && _extra == other._extra;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	// Attr contains all the information about a note that should not be deduplicated
 | 
			
		||||
	struct Attr {
 | 
			
		||||
		Sort sort;
 | 
			
		||||
	};
 | 
			
		||||
	// our specialised version of ComputeGraph
 | 
			
		||||
	// the sparse_attr IdString stores a naming suggestion, retrieved with name()
 | 
			
		||||
	// the key is currently used to identify the nodes that represent output and next state values
 | 
			
		||||
	// the bool is true for next state values
 | 
			
		||||
	using Graph = ComputeGraph<NodeData, Attr, IdString, std::pair<IdString, bool>>;
 | 
			
		||||
	Graph _graph;
 | 
			
		||||
	dict<IdString, Sort> _inputs;
 | 
			
		||||
| 
						 | 
				
			
			@ -132,53 +220,17 @@ private:
 | 
			
		|||
	}
 | 
			
		||||
public:
 | 
			
		||||
	class Factory;
 | 
			
		||||
	// Node is an immutable reference to a FunctionalIR node
 | 
			
		||||
	class Node {
 | 
			
		||||
		friend class Factory;
 | 
			
		||||
		friend class FunctionalIR;
 | 
			
		||||
		Graph::Ref _ref;
 | 
			
		||||
		explicit Node(Graph::Ref ref) : _ref(ref) { }
 | 
			
		||||
		operator Graph::Ref() { return _ref; }
 | 
			
		||||
		template<class NodePrinter> struct PrintVisitor {
 | 
			
		||||
			NodePrinter np;
 | 
			
		||||
			PrintVisitor(NodePrinter np) : np(np) { }
 | 
			
		||||
			std::string buf(Node, Node n) { return "buf(" + np(n) + ")"; }
 | 
			
		||||
			std::string slice(Node, Node a, int, int offset, int out_width) { return "slice(" + np(a) + ", " + std::to_string(offset) + ", " + std::to_string(out_width) + ")"; }
 | 
			
		||||
			std::string zero_extend(Node, Node a, int, int out_width) { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
 | 
			
		||||
			std::string sign_extend(Node, Node a, int, int out_width) { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
 | 
			
		||||
			std::string concat(Node, Node a, int, Node b, int) { return "concat(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string add(Node, Node a, Node b, int) { return "add(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string sub(Node, Node a, Node b, int) { return "sub(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string mul(Node, Node a, Node b, int) { return "mul(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string unsigned_div(Node, Node a, Node b, int) { return "unsigned_div(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string unsigned_mod(Node, Node a, Node b, int) { return "unsigned_mod(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string bitwise_and(Node, Node a, Node b, int) { return "bitwise_and(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string bitwise_or(Node, Node a, Node b, int) { return "bitwise_or(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string bitwise_xor(Node, Node a, Node b, int) { return "bitwise_xor(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string bitwise_not(Node, Node a, int) { return "bitwise_not(" + np(a) + ")"; }
 | 
			
		||||
			std::string unary_minus(Node, Node a, int) { return "unary_minus(" + np(a) + ")"; }
 | 
			
		||||
			std::string reduce_and(Node, Node a, int) { return "reduce_and(" + np(a) + ")"; }
 | 
			
		||||
			std::string reduce_or(Node, Node a, int) { return "reduce_or(" + np(a) + ")"; }
 | 
			
		||||
			std::string reduce_xor(Node, Node a, int) { return "reduce_xor(" + np(a) + ")"; }
 | 
			
		||||
			std::string equal(Node, Node a, Node b, int) { return "equal(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string not_equal(Node, Node a, Node b, int) { return "not_equal(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string signed_greater_than(Node, Node a, Node b, int) { return "signed_greater_than(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string signed_greater_equal(Node, Node a, Node b, int) { return "signed_greater_equal(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string unsigned_greater_than(Node, Node a, Node b, int) { return "unsigned_greater_than(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string unsigned_greater_equal(Node, Node a, Node b, int) { return "unsigned_greater_equal(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string logical_shift_left(Node, Node a, Node b, int, int) { return "logical_shift_left(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string logical_shift_right(Node, Node a, Node b, int, int) { return "logical_shift_right(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string arithmetic_shift_right(Node, Node a, Node b, int, int) { return "arithmetic_shift_right(" + np(a) + ", " + np(b) + ")"; }
 | 
			
		||||
			std::string mux(Node, Node a, Node b, Node s, int) { return "mux(" + np(a) + ", " + np(b) + ", " + np(s) + ")"; }
 | 
			
		||||
			std::string pmux(Node, Node a, Node b, Node s, int, int) { return "pmux(" + np(a) + ", " + np(b) + ", " + np(s) + ")"; }
 | 
			
		||||
			std::string constant(Node, RTLIL::Const value) { return "constant(" + value.as_string() + ")"; }
 | 
			
		||||
			std::string input(Node, IdString name) { return "input(" + name.str() + ")"; }
 | 
			
		||||
			std::string state(Node, IdString name) { return "state(" + name.str() + ")"; }
 | 
			
		||||
			std::string memory_read(Node, Node mem, Node addr, int, int) { return "memory_read(" + np(mem) + ", " + np(addr) + ")"; }
 | 
			
		||||
			std::string memory_write(Node, Node mem, Node addr, Node data, int, int) { return "memory_write(" + np(mem) + ", " + np(addr) + ", " + np(data) + ")"; }
 | 
			
		||||
			std::string undriven(Node, int width) { return "undriven(" + std::to_string(width) + ")"; }
 | 
			
		||||
		};
 | 
			
		||||
		Graph::ConstRef _ref;
 | 
			
		||||
		explicit Node(Graph::ConstRef ref) : _ref(ref) { }
 | 
			
		||||
		explicit operator Graph::ConstRef() { return _ref; }
 | 
			
		||||
	public:
 | 
			
		||||
		// the node's index. may change if nodes are added or removed
 | 
			
		||||
		int id() const { return _ref.index(); }
 | 
			
		||||
		// a name suggestion for the node, which need not be unique
 | 
			
		||||
		IdString name() const {
 | 
			
		||||
			if(_ref.has_sparse_attr())
 | 
			
		||||
				return _ref.sparse_attr();
 | 
			
		||||
| 
						 | 
				
			
			@ -187,55 +239,135 @@ public:
 | 
			
		|||
		}
 | 
			
		||||
		Fn fn() const { return _ref.function().fn(); }
 | 
			
		||||
		Sort sort() const { return _ref.attr().sort; }
 | 
			
		||||
		// returns the width of a bitvector node, errors out for other nodes
 | 
			
		||||
		int width() const { return sort().width(); }
 | 
			
		||||
		size_t arg_count() const { return _ref.size(); }
 | 
			
		||||
		Node arg(int n) const { return Node(_ref.arg(n)); }
 | 
			
		||||
		// visit calls the appropriate visitor method depending on the type of the node
 | 
			
		||||
		template<class Visitor> auto visit(Visitor v) const
 | 
			
		||||
		{
 | 
			
		||||
			// currently templated but could be switched to AbstractVisitor &
 | 
			
		||||
			switch(_ref.function().fn()) {
 | 
			
		||||
			case Fn::invalid: log_error("invalid node in visit"); break;
 | 
			
		||||
			case Fn::buf: return v.buf(*this, arg(0)); break;
 | 
			
		||||
			case Fn::slice: return v.slice(*this, arg(0), arg(0).width(), _ref.function().as_int(), sort().width()); break;
 | 
			
		||||
			case Fn::zero_extend: return v.zero_extend(*this, arg(0), arg(0).width(), width()); break;
 | 
			
		||||
			case Fn::sign_extend: return v.sign_extend(*this, arg(0), arg(0).width(), width()); break;
 | 
			
		||||
			case Fn::concat: return v.concat(*this, arg(0), arg(0).width(), arg(1), arg(1).width()); break;
 | 
			
		||||
			case Fn::add: return v.add(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::sub: return v.sub(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::mul: return v.mul(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::unsigned_div: return v.unsigned_div(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::unsigned_mod: return v.unsigned_mod(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::bitwise_and: return v.bitwise_and(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::bitwise_or: return v.bitwise_or(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::bitwise_xor: return v.bitwise_xor(*this, arg(0), arg(1), sort().width()); break;
 | 
			
		||||
			case Fn::bitwise_not: return v.bitwise_not(*this, arg(0), sort().width()); break;
 | 
			
		||||
			case Fn::unary_minus: return v.unary_minus(*this, arg(0), sort().width()); break;
 | 
			
		||||
			case Fn::reduce_and: return v.reduce_and(*this, arg(0), arg(0).width()); break;
 | 
			
		||||
			case Fn::reduce_or: return v.reduce_or(*this, arg(0), arg(0).width()); break;
 | 
			
		||||
			case Fn::reduce_xor: return v.reduce_xor(*this, arg(0), arg(0).width()); break;
 | 
			
		||||
			case Fn::equal: return v.equal(*this, arg(0), arg(1), arg(0).width()); break;
 | 
			
		||||
			case Fn::not_equal: return v.not_equal(*this, arg(0), arg(1), arg(0).width()); break;
 | 
			
		||||
			case Fn::signed_greater_than: return v.signed_greater_than(*this, arg(0), arg(1), arg(0).width()); break; 
 | 
			
		||||
			case Fn::signed_greater_equal: return v.signed_greater_equal(*this, arg(0), arg(1), arg(0).width()); break;
 | 
			
		||||
			case Fn::unsigned_greater_than: return v.unsigned_greater_than(*this, arg(0), arg(1), arg(0).width()); break; 
 | 
			
		||||
			case Fn::unsigned_greater_equal: return v.unsigned_greater_equal(*this, arg(0), arg(1), arg(0).width()); break;
 | 
			
		||||
			case Fn::logical_shift_left: return v.logical_shift_left(*this, arg(0), arg(1), arg(0).width(), arg(1).width()); break;
 | 
			
		||||
			case Fn::logical_shift_right: return v.logical_shift_right(*this, arg(0), arg(1), arg(0).width(), arg(1).width()); break;
 | 
			
		||||
			case Fn::arithmetic_shift_right: return v.arithmetic_shift_right(*this, arg(0), arg(1), arg(0).width(), arg(1).width()); break;
 | 
			
		||||
			case Fn::mux: return v.mux(*this, arg(0), arg(1), arg(2), arg(0).width()); break;
 | 
			
		||||
			case Fn::pmux: return v.pmux(*this, arg(0), arg(1), arg(2), arg(0).width(), arg(2).width()); break;
 | 
			
		||||
			case Fn::slice: return v.slice(*this, arg(0), _ref.function().as_int(), sort().width()); break;
 | 
			
		||||
			case Fn::zero_extend: return v.zero_extend(*this, arg(0), width()); break;
 | 
			
		||||
			case Fn::sign_extend: return v.sign_extend(*this, arg(0), width()); break;
 | 
			
		||||
			case Fn::concat: return v.concat(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::add: return v.add(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::sub: return v.sub(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::mul: return v.mul(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::unsigned_div: return v.unsigned_div(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::unsigned_mod: return v.unsigned_mod(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::bitwise_and: return v.bitwise_and(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::bitwise_or: return v.bitwise_or(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::bitwise_xor: return v.bitwise_xor(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::bitwise_not: return v.bitwise_not(*this, arg(0)); break;
 | 
			
		||||
			case Fn::unary_minus: return v.unary_minus(*this, arg(0)); break;
 | 
			
		||||
			case Fn::reduce_and: return v.reduce_and(*this, arg(0)); break;
 | 
			
		||||
			case Fn::reduce_or: return v.reduce_or(*this, arg(0)); break;
 | 
			
		||||
			case Fn::reduce_xor: return v.reduce_xor(*this, arg(0)); break;
 | 
			
		||||
			case Fn::equal: return v.equal(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::not_equal: return v.not_equal(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::signed_greater_than: return v.signed_greater_than(*this, arg(0), arg(1)); break; 
 | 
			
		||||
			case Fn::signed_greater_equal: return v.signed_greater_equal(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::unsigned_greater_than: return v.unsigned_greater_than(*this, arg(0), arg(1)); break; 
 | 
			
		||||
			case Fn::unsigned_greater_equal: return v.unsigned_greater_equal(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::logical_shift_left: return v.logical_shift_left(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::logical_shift_right: return v.logical_shift_right(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::arithmetic_shift_right: return v.arithmetic_shift_right(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::mux: return v.mux(*this, arg(0), arg(1), arg(2)); break;
 | 
			
		||||
			case Fn::pmux: return v.pmux(*this, arg(0), arg(1), arg(2)); break;
 | 
			
		||||
			case Fn::constant: return v.constant(*this, _ref.function().as_const()); break;
 | 
			
		||||
			case Fn::input: return v.input(*this, _ref.function().as_idstring()); break;
 | 
			
		||||
			case Fn::state: return v.state(*this, _ref.function().as_idstring()); break;
 | 
			
		||||
			case Fn::memory_read: return v.memory_read(*this, arg(0), arg(1), arg(1).width(), width()); break;
 | 
			
		||||
			case Fn::memory_write: return v.memory_write(*this, arg(0), arg(1), arg(2), arg(1).width(), arg(2).width()); break;
 | 
			
		||||
			case Fn::memory_read: return v.memory_read(*this, arg(0), arg(1)); break;
 | 
			
		||||
			case Fn::memory_write: return v.memory_write(*this, arg(0), arg(1), arg(2)); break;
 | 
			
		||||
			case Fn::multiple: log_error("multiple in visit"); break;
 | 
			
		||||
			case Fn::undriven: return v.undriven(*this, width()); break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		template<class NodePrinter> std::string to_string(NodePrinter np)
 | 
			
		||||
		{
 | 
			
		||||
			return visit(PrintVisitor(np));
 | 
			
		||||
		}
 | 
			
		||||
		std::string to_string();
 | 
			
		||||
		std::string to_string(std::function<std::string(Node)>);
 | 
			
		||||
	};
 | 
			
		||||
	// AbstractVisitor provides an abstract base class for visitors
 | 
			
		||||
	template<class T> struct AbstractVisitor {
 | 
			
		||||
		virtual T buf(Node self, Node n) = 0;
 | 
			
		||||
		virtual T slice(Node self, Node a, int offset, int out_width) = 0;
 | 
			
		||||
		virtual T zero_extend(Node self, Node a, int out_width) = 0;
 | 
			
		||||
		virtual T sign_extend(Node self, Node a, int out_width) = 0;
 | 
			
		||||
		virtual T concat(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T add(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T sub(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T mul(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T unsigned_div(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T unsigned_mod(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T bitwise_and(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T bitwise_or(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T bitwise_xor(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T bitwise_not(Node self, Node a) = 0;
 | 
			
		||||
		virtual T unary_minus(Node self, Node a) = 0;
 | 
			
		||||
		virtual T reduce_and(Node self, Node a) = 0;
 | 
			
		||||
		virtual T reduce_or(Node self, Node a) = 0;
 | 
			
		||||
		virtual T reduce_xor(Node self, Node a) = 0;
 | 
			
		||||
		virtual T equal(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T not_equal(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T signed_greater_than(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T signed_greater_equal(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T unsigned_greater_than(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T unsigned_greater_equal(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T logical_shift_left(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T logical_shift_right(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T arithmetic_shift_right(Node self, Node a, Node b) = 0;
 | 
			
		||||
		virtual T mux(Node self, Node a, Node b, Node s) = 0;
 | 
			
		||||
		virtual T pmux(Node self, Node a, Node b, Node s) = 0;
 | 
			
		||||
		virtual T constant(Node self, RTLIL::Const value) = 0;
 | 
			
		||||
		virtual T input(Node self, IdString name) = 0;
 | 
			
		||||
		virtual T state(Node self, IdString name) = 0;
 | 
			
		||||
		virtual T memory_read(Node self, Node mem, Node addr) = 0;
 | 
			
		||||
		virtual T memory_write(Node self, Node mem, Node addr, Node data) = 0;
 | 
			
		||||
		virtual T undriven(Node self, int width) = 0;
 | 
			
		||||
	};
 | 
			
		||||
	// DefaultVisitor provides defaults for all visitor methods which just calls default_handler
 | 
			
		||||
	template<class T> struct DefaultVisitor : public AbstractVisitor<T> {
 | 
			
		||||
		virtual T default_handler(Node self) = 0;
 | 
			
		||||
		T buf(Node self, Node) override { return default_handler(self); }
 | 
			
		||||
		T slice(Node self, Node, int, int) override { return default_handler(self); }
 | 
			
		||||
		T zero_extend(Node self, Node, int) override { return default_handler(self); }
 | 
			
		||||
		T sign_extend(Node self, Node, int) override { return default_handler(self); }
 | 
			
		||||
		T concat(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T add(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T sub(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T mul(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T unsigned_div(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T unsigned_mod(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T bitwise_and(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T bitwise_or(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T bitwise_xor(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T bitwise_not(Node self, Node) override { return default_handler(self); }
 | 
			
		||||
		T unary_minus(Node self, Node) override { return default_handler(self); }
 | 
			
		||||
		T reduce_and(Node self, Node) override { return default_handler(self); }
 | 
			
		||||
		T reduce_or(Node self, Node) override { return default_handler(self); }
 | 
			
		||||
		T reduce_xor(Node self, Node) override { return default_handler(self); }
 | 
			
		||||
		T equal(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T not_equal(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T signed_greater_than(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T signed_greater_equal(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T unsigned_greater_than(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T unsigned_greater_equal(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T logical_shift_left(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T logical_shift_right(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T arithmetic_shift_right(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T mux(Node self, Node, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T pmux(Node self, Node, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T constant(Node self, RTLIL::Const) override { return default_handler(self); }
 | 
			
		||||
		T input(Node self, IdString) override { return default_handler(self); }
 | 
			
		||||
		T state(Node self, IdString) override { return default_handler(self); }
 | 
			
		||||
		T memory_read(Node self, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T memory_write(Node self, Node, Node, Node) override { return default_handler(self); }
 | 
			
		||||
		T undriven(Node self, int) override { return default_handler(self); }
 | 
			
		||||
	};
 | 
			
		||||
	// a factory is used to modify a FunctionalIR. it creates new nodes and allows for some modification of existing nodes.
 | 
			
		||||
	class Factory {
 | 
			
		||||
		FunctionalIR &_ir;
 | 
			
		||||
		friend class FunctionalIR;
 | 
			
		||||
| 
						 | 
				
			
			@ -243,19 +375,29 @@ public:
 | 
			
		|||
		Node add(NodeData &&fn, Sort &&sort, std::initializer_list<Node> args) {
 | 
			
		||||
			Graph::Ref ref = _ir._graph.add(std::move(fn), {std::move(sort)});
 | 
			
		||||
			for (auto arg : args)
 | 
			
		||||
				ref.append_arg(Graph::Ref(arg));
 | 
			
		||||
				ref.append_arg(Graph::ConstRef(arg));
 | 
			
		||||
			return Node(ref);
 | 
			
		||||
		}
 | 
			
		||||
		Graph::Ref mutate(Node n) {
 | 
			
		||||
			return _ir._graph[n._ref.index()];
 | 
			
		||||
		}
 | 
			
		||||
		void check_basic_binary(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && a.sort() == b.sort()); }
 | 
			
		||||
		void check_shift(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && b.sort().is_signal()); }
 | 
			
		||||
		void check_unary(Node const &a) { log_assert(a.sort().is_signal()); }
 | 
			
		||||
	public:
 | 
			
		||||
		Node slice(Node a, int, int offset, int out_width) {
 | 
			
		||||
			log_assert(a.sort().is_signal() && offset + out_width <= a.sort().width());
 | 
			
		||||
			if(offset == 0 && out_width == a.width())
 | 
			
		||||
				return a;
 | 
			
		||||
			return add(NodeData(Fn::slice, offset), Sort(out_width), {a});
 | 
			
		||||
		}
 | 
			
		||||
		Node extend(Node a, int, int out_width, bool is_signed) {
 | 
			
		||||
			log_assert(a.sort().is_signal() && a.sort().width() < out_width);
 | 
			
		||||
			int in_width = a.sort().width();
 | 
			
		||||
			log_assert(a.sort().is_signal());
 | 
			
		||||
			if(in_width == out_width)
 | 
			
		||||
				return a;
 | 
			
		||||
			if(in_width < out_width)
 | 
			
		||||
				return slice(a, in_width, 0, out_width);
 | 
			
		||||
			if(is_signed)
 | 
			
		||||
				return add(Fn::sign_extend, Sort(out_width), {a});
 | 
			
		||||
			else
 | 
			
		||||
| 
						 | 
				
			
			@ -275,9 +417,24 @@ public:
 | 
			
		|||
		Node bitwise_xor(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::bitwise_xor, a.sort(), {a, b}); }
 | 
			
		||||
		Node bitwise_not(Node a, int) { check_unary(a); return add(Fn::bitwise_not, a.sort(), {a}); }
 | 
			
		||||
		Node unary_minus(Node a, int) { check_unary(a); return add(Fn::unary_minus, a.sort(), {a}); }
 | 
			
		||||
		Node reduce_and(Node a, int) { check_unary(a); return add(Fn::reduce_and, Sort(1), {a}); }
 | 
			
		||||
		Node reduce_or(Node a, int) { check_unary(a); return add(Fn::reduce_or, Sort(1), {a}); }
 | 
			
		||||
		Node reduce_xor(Node a, int) { check_unary(a); return add(Fn::reduce_xor, Sort(1), {a}); }
 | 
			
		||||
		Node reduce_and(Node a, int) {
 | 
			
		||||
			check_unary(a);
 | 
			
		||||
			if(a.width() == 1)
 | 
			
		||||
				return a;
 | 
			
		||||
			return add(Fn::reduce_and, Sort(1), {a});
 | 
			
		||||
		}
 | 
			
		||||
		Node reduce_or(Node a, int) {
 | 
			
		||||
			check_unary(a);
 | 
			
		||||
			if(a.width() == 1)
 | 
			
		||||
				return a;
 | 
			
		||||
			return add(Fn::reduce_or, Sort(1), {a});
 | 
			
		||||
		}
 | 
			
		||||
		Node reduce_xor(Node a, int) { 
 | 
			
		||||
			check_unary(a);
 | 
			
		||||
			if(a.width() == 1)
 | 
			
		||||
				return a;
 | 
			
		||||
			return add(Fn::reduce_xor, Sort(1), {a});
 | 
			
		||||
		}
 | 
			
		||||
		Node equal(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::equal, Sort(1), {a, b}); }
 | 
			
		||||
		Node not_equal(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::not_equal, Sort(1), {a, b}); }
 | 
			
		||||
		Node signed_greater_than(Node a, Node b, int) { check_basic_binary(a, b); return add(Fn::signed_greater_than, Sort(1), {a, b}); }
 | 
			
		||||
| 
						 | 
				
			
			@ -313,7 +470,7 @@ public:
 | 
			
		|||
		void update_pending(Node node, Node value) {
 | 
			
		||||
			log_assert(node._ref.function() == Fn::buf && node._ref.size() == 0);
 | 
			
		||||
			log_assert(node.sort() == value.sort());
 | 
			
		||||
			node._ref.append_arg(value._ref);
 | 
			
		||||
			mutate(node).append_arg(value._ref);
 | 
			
		||||
		} 
 | 
			
		||||
		Node input(IdString name, int width) {
 | 
			
		||||
			_ir.add_input(name, Sort(width));
 | 
			
		||||
| 
						 | 
				
			
			@ -333,7 +490,7 @@ public:
 | 
			
		|||
		Node multiple(vector<Node> args, int width) {
 | 
			
		||||
			auto node = add(Fn::multiple, Sort(width), {});
 | 
			
		||||
			for(const auto &arg : args)
 | 
			
		||||
				node._ref.append_arg(arg._ref);
 | 
			
		||||
				mutate(node).append_arg(arg._ref);
 | 
			
		||||
			return node;
 | 
			
		||||
		}
 | 
			
		||||
		Node undriven(int width) {
 | 
			
		||||
| 
						 | 
				
			
			@ -341,18 +498,18 @@ public:
 | 
			
		|||
		}
 | 
			
		||||
		void declare_output(Node node, IdString name, int width) {
 | 
			
		||||
			_ir.add_output(name, Sort(width));
 | 
			
		||||
			node._ref.assign_key({name, false});
 | 
			
		||||
			mutate(node).assign_key({name, false});
 | 
			
		||||
		}
 | 
			
		||||
		void declare_state(Node node, IdString name, int width) {
 | 
			
		||||
			_ir.add_state(name, Sort(width));
 | 
			
		||||
			node._ref.assign_key({name, true});
 | 
			
		||||
			mutate(node).assign_key({name, true});
 | 
			
		||||
		}
 | 
			
		||||
		void declare_state_memory(Node node, IdString name, int addr_width, int data_width) {
 | 
			
		||||
			_ir.add_state(name, Sort(addr_width, data_width));
 | 
			
		||||
			node._ref.assign_key({name, true});
 | 
			
		||||
			mutate(node).assign_key({name, true});
 | 
			
		||||
		}
 | 
			
		||||
		void suggest_name(Node node, IdString name) {
 | 
			
		||||
			node._ref.sparse_attr() = name;
 | 
			
		||||
			mutate(node).sparse_attr() = name;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	static FunctionalIR from_module(Module *module);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue