diff --git a/backends/functional/cxx.cc b/backends/functional/cxx.cc index 4ab9d53a1d4..920ccf363bc 100644 --- a/backends/functional/cxx.cc +++ b/backends/functional/cxx.cc @@ -98,7 +98,7 @@ struct CxxStruct { } }; -template struct CxxPrintVisitor { +template struct CxxPrintVisitor : public FunctionalIR::AbstractVisitor { using Node = FunctionalIR::Node; CxxWriter &f; NodePrinter np; @@ -108,36 +108,36 @@ template struct CxxPrintVisitor { template void print(const char *fmt, Args&&... args) { f.print_with(np, fmt, std::forward(args)...); } - void buf(Node, Node n) { print("{}", n); } - void slice(Node, Node a, int, int offset, int out_width) { print("{0}.slice<{2}>({1})", a, offset, out_width); } - void zero_extend(Node, Node a, int, int out_width) { print("{}.zero_extend<{}>()", a, out_width); } - void sign_extend(Node, Node a, int, int out_width) { print("{}.sign_extend<{}>()", a, out_width); } - void concat(Node, Node a, int, Node b, int) { print("{}.concat({})", a, b); } - void add(Node, Node a, Node b, int) { print("{} + {}", a, b); } - void sub(Node, Node a, Node b, int) { print("{} - {}", a, b); } - void mul(Node, Node a, Node b, int) { print("{} * {}", a, b); } - void unsigned_div(Node, Node a, Node b, int) { print("{} / {}", a, b); } - void unsigned_mod(Node, Node a, Node b, int) { print("{} % {}", a, b); } - void bitwise_and(Node, Node a, Node b, int) { print("{} & {}", a, b); } - void bitwise_or(Node, Node a, Node b, int) { print("{} | {}", a, b); } - void bitwise_xor(Node, Node a, Node b, int) { print("{} ^ {}", a, b); } - void bitwise_not(Node, Node a, int) { print("~{}", a); } - void unary_minus(Node, Node a, int) { print("-{}", a); } - void reduce_and(Node, Node a, int) { print("{}.all()", a); } - void reduce_or(Node, Node a, int) { print("{}.any()", a); } - void reduce_xor(Node, Node a, int) { print("{}.parity()", a); } - void equal(Node, Node a, Node b, int) { print("{} == {}", a, b); } - void not_equal(Node, Node a, Node b, int) { print("{} != {}", a, b); } - void signed_greater_than(Node, Node a, Node b, int) { print("{}.signed_greater_than({})", a, b); } - void signed_greater_equal(Node, Node a, Node b, int) { print("{}.signed_greater_equal({})", a, b); } - void unsigned_greater_than(Node, Node a, Node b, int) { print("{} > {}", a, b); } - void unsigned_greater_equal(Node, Node a, Node b, int) { print("{} >= {}", a, b); } - void logical_shift_left(Node, Node a, Node b, int, int) { print("{} << {}", a, b); } - void logical_shift_right(Node, Node a, Node b, int, int) { print("{} >> {}", a, b); } - void arithmetic_shift_right(Node, Node a, Node b, int, int) { print("{}.arithmetic_shift_right({})", a, b); } - void mux(Node, Node a, Node b, Node s, int) { print("{2}.any() ? {1} : {0}", a, b, s); } - void pmux(Node, Node a, Node b, Node s, int, int) { print("{0}.pmux({1}, {2})", a, b, s); } - void constant(Node, RTLIL::Const value) { + void buf(Node, Node n) override { print("{}", n); } + void slice(Node, Node a, int offset, int out_width) override { print("{0}.slice<{2}>({1})", a, offset, out_width); } + void zero_extend(Node, Node a, int out_width) override { print("{}.zero_extend<{}>()", a, out_width); } + void sign_extend(Node, Node a, int out_width) override { print("{}.sign_extend<{}>()", a, out_width); } + void concat(Node, Node a, Node b) override { print("{}.concat({})", a, b); } + void add(Node, Node a, Node b) override { print("{} + {}", a, b); } + void sub(Node, Node a, Node b) override { print("{} - {}", a, b); } + void mul(Node, Node a, Node b) override { print("{} * {}", a, b); } + void unsigned_div(Node, Node a, Node b) override { print("{} / {}", a, b); } + void unsigned_mod(Node, Node a, Node b) override { print("{} % {}", a, b); } + void bitwise_and(Node, Node a, Node b) override { print("{} & {}", a, b); } + void bitwise_or(Node, Node a, Node b) override { print("{} | {}", a, b); } + void bitwise_xor(Node, Node a, Node b) override { print("{} ^ {}", a, b); } + void bitwise_not(Node, Node a) override { print("~{}", a); } + void unary_minus(Node, Node a) override { print("-{}", a); } + void reduce_and(Node, Node a) override { print("{}.all()", a); } + void reduce_or(Node, Node a) override { print("{}.any()", a); } + void reduce_xor(Node, Node a) override { print("{}.parity()", a); } + void equal(Node, Node a, Node b) override { print("{} == {}", a, b); } + void not_equal(Node, Node a, Node b) override { print("{} != {}", a, b); } + void signed_greater_than(Node, Node a, Node b) override { print("{}.signed_greater_than({})", a, b); } + void signed_greater_equal(Node, Node a, Node b) override { print("{}.signed_greater_equal({})", a, b); } + void unsigned_greater_than(Node, Node a, Node b) override { print("{} > {}", a, b); } + void unsigned_greater_equal(Node, Node a, Node b) override { print("{} >= {}", a, b); } + void logical_shift_left(Node, Node a, Node b) override { print("{} << {}", a, b); } + void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); } + void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); } + void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); } + void pmux(Node, Node a, Node b, Node s) override { print("{0}.pmux({1}, {2})", a, b, s); } + void constant(Node, RTLIL::Const value) override { std::stringstream ss; bool multiple = value.size() > 32; ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase; @@ -151,11 +151,11 @@ template struct CxxPrintVisitor { ss << ")"; print("{}", ss.str()); } - void input(Node, IdString name) { print("input.{}", input_struct[name]); } - void state(Node, IdString name) { print("current_state.{}", state_struct[name]); } - void memory_read(Node, Node mem, Node addr, int, int) { print("{}.read({})", mem, addr); } - void memory_write(Node, Node mem, Node addr, Node data, int, int) { print("{}.write({}, {})", mem, addr, data); } - void undriven(Node, int width) { print("Signal<{}>(0)", width); } + void input(Node, IdString name) override { print("input.{}", input_struct[name]); } + void state(Node, IdString name) override { print("current_state.{}", state_struct[name]); } + void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); } + void memory_write(Node, Node mem, Node addr, Node data) override { print("{}.write({}, {})", mem, addr, data); } + void undriven(Node, int width) override { print("Signal<{}>(0)", width); } }; struct CxxModule { diff --git a/backends/functional/smtlib.cc b/backends/functional/smtlib.cc index 113fb710080..c35a48dd809 100644 --- a/backends/functional/smtlib.cc +++ b/backends/functional/smtlib.cc @@ -109,7 +109,7 @@ class SmtStruct { } }; -struct SmtPrintVisitor { +struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor { using Node = FunctionalIR::Node; std::function n; SmtStruct &input_struct; @@ -134,35 +134,35 @@ struct SmtPrintVisitor { return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); } - SExpr buf(Node, Node a) { return n(a); } - SExpr slice(Node, Node a, int, int offset, int out_width) { return extract(n(a), offset, out_width); } - SExpr zero_extend(Node, Node a, int, int out_width) { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } - SExpr sign_extend(Node, Node a, int, int out_width) { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } - SExpr concat(Node, Node a, int, Node b, int) { return list("concat", n(b), n(a)); } - SExpr add(Node, Node a, Node b, int) { return list("bvadd", n(a), n(b)); } - SExpr sub(Node, Node a, Node b, int) { return list("bvsub", n(a), n(b)); } - SExpr mul(Node, Node a, Node b, int) { return list("bvmul", n(a), n(b)); } - SExpr unsigned_div(Node, Node a, Node b, int) { return list("bvudiv", n(a), n(b)); } - SExpr unsigned_mod(Node, Node a, Node b, int) { return list("bvurem", n(a), n(b)); } - SExpr bitwise_and(Node, Node a, Node b, int) { return list("bvand", n(a), n(b)); } - SExpr bitwise_or(Node, Node a, Node b, int) { return list("bvor", n(a), n(b)); } - SExpr bitwise_xor(Node, Node a, Node b, int) { return list("bvxor", n(a), n(b)); } - SExpr bitwise_not(Node, Node a, int) { return list("bvnot", n(a)); } - SExpr unary_minus(Node, Node a, int) { return list("bvneg", n(a)); } - SExpr reduce_and(Node, Node a, int) { return from_bool(list("=", n(a), literal(RTLIL::Const(State::S1, a.width())))); } - SExpr reduce_or(Node, Node a, int) { return from_bool(list("distinct", n(a), literal(RTLIL::Const(State::S0, a.width())))); } - SExpr reduce_xor(Node, Node a, int) { + SExpr buf(Node, Node a) override { return n(a); } + SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); } + SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } + SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } + SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } + SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } + SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } + SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); } + SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); } + SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); } + SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); } + SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); } + SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } + SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } + SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } + SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), literal(RTLIL::Const(State::S1, a.width())))); } + SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), literal(RTLIL::Const(State::S0, a.width())))); } + SExpr reduce_xor(Node, Node a) override { vector s { "bvxor" }; for(int i = 0; i < a.width(); i++) s.push_back(extract(n(a), i)); return s; } - SExpr equal(Node, Node a, Node b, int) { return from_bool(list("=", n(a), n(b))); } - SExpr not_equal(Node, Node a, Node b, int) { return from_bool(list("distinct", n(a), n(b))); } - SExpr signed_greater_than(Node, Node a, Node b, int) { return from_bool(list("bvsgt", n(a), n(b))); } - SExpr signed_greater_equal(Node, Node a, Node b, int) { return from_bool(list("bvsge", n(a), n(b))); } - SExpr unsigned_greater_than(Node, Node a, Node b, int) { return from_bool(list("bvugt", n(a), n(b))); } - SExpr unsigned_greater_equal(Node, Node a, Node b, int) { return from_bool(list("bvuge", n(a), n(b))); } + SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); } + SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); } + SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } + SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } + SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } + SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); } SExpr extend(SExpr &&a, int in_width, int out_width) { if(in_width < out_width) @@ -170,24 +170,24 @@ struct SmtPrintVisitor { else return std::move(a); } - SExpr logical_shift_left(Node, Node a, Node b, int, int) { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } - SExpr logical_shift_right(Node, Node a, Node b, int, int) { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } - SExpr arithmetic_shift_right(Node, Node a, Node b, int, int) { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } - SExpr mux(Node, Node a, Node b, Node s, int) { return list("ite", to_bool(n(s)), n(b), n(a)); } - SExpr pmux(Node, Node a, Node b, Node s, int, int) { + SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } + SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } + SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } + SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } + SExpr pmux(Node, Node a, Node b, Node s) override { SExpr rv = n(a); for(int i = 0; i < s.width(); i++) rv = list("ite", to_bool(extract(n(s), i)), extract(n(b), a.width() * i, a.width()), rv); return rv; } - SExpr constant(Node, RTLIL::Const value) { return literal(value); } - SExpr memory_read(Node, Node mem, Node addr, int, int) { return list("select", n(mem), n(addr)); } - SExpr memory_write(Node, Node mem, Node addr, Node data, int, int) { return list("store", n(mem), n(addr), n(data)); } + SExpr constant(Node, RTLIL::Const value) override { return literal(value); } + SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } + SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } - SExpr input(Node, IdString name) { return input_struct.access("inputs", name); } - SExpr state(Node, IdString name) { return state_struct.access("state", name); } + SExpr input(Node, IdString name) override { return input_struct.access("inputs", name); } + SExpr state(Node, IdString name) override { return state_struct.access("state", name); } - SExpr undriven(Node, int width) { return literal(RTLIL::Const(State::S0, width)); } + SExpr undriven(Node, int width) override { return literal(RTLIL::Const(State::S0, width)); } }; struct SmtModule { diff --git a/kernel/functional.h b/kernel/functional.h index 09a8826433c..09f66c9813b 100644 --- a/kernel/functional.h +++ b/kernel/functional.h @@ -109,7 +109,7 @@ struct ComputeGraph { auto found = graph_->sparse_attrs.find(index_); log_assert(found != graph_->sparse_attrs.end()); - return *found; + return found->second; } }; diff --git a/kernel/functionalir.cc b/kernel/functionalir.cc index b8fa6b88e5f..9b8076a0ef7 100644 --- a/kernel/functionalir.cc +++ b/kernel/functionalir.cc @@ -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 { + using Node = FunctionalIR::Node; + std::function np; + PrintVisitor(std::function 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 np) +{ + return visit(PrintVisitor(np)); +} + template 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 @@ class CellSimplifier { 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 @@ class CellSimplifier { 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 @@ class CellSimplifier { // 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; diff --git a/kernel/functionalir.h b/kernel/functionalir.h index 99fc872a81a..e586695695a 100644 --- a/kernel/functionalir.h +++ b/kernel/functionalir.h @@ -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> _v; public: @@ -77,13 +155,18 @@ class FunctionalIR { 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 @@ class FunctionalIR { 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>; Graph _graph; dict _inputs; @@ -132,53 +220,17 @@ class FunctionalIR { } 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 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 @@ class FunctionalIR { } 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 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 std::string to_string(NodePrinter np) - { - return visit(PrintVisitor(np)); - } + std::string to_string(); + std::string to_string(std::function); + }; + // AbstractVisitor provides an abstract base class for visitors + template 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 struct DefaultVisitor : public AbstractVisitor { + 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 @@ class FunctionalIR { Node add(NodeData &&fn, Sort &&sort, std::initializer_list 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 @@ class FunctionalIR { 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 @@ class FunctionalIR { 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 @@ class FunctionalIR { Node multiple(vector 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 @@ class FunctionalIR { } 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);