From c429aef60f60cf405cf9e35e019446b37c430617 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 22 Feb 2025 17:14:13 +1300 Subject: [PATCH] docs/rosette: More intro of SMT-LIB backend As the intro paragraph (now) says: > This section will introduce the SMT-LIB functional backend and what changes are needed... The example is intended to be read without prior knowledge of the SMT-LIB backend, but the previous version glossed over a lot and instead focused on *just* what was changed. This version should now be easier to follow without prior knowledge, while still being able to learn enough about the `Smt` version to adapt it to a different s-expression target that isn't Rosette. Also adds a few `literalinclude`s of smtlib.cc, which is now copied to `docs/source/generated` along with producing the rosette diff on the fly (which now also has up to 20 lines of context, enabling the full `Module::write()` diff to be literal included). --- Makefile | 14 +- .../code_examples/functional/rosette.diff | 372 ------------------ .../extending_yosys/functional_ir.rst | 149 ++++--- 3 files changed, 98 insertions(+), 437 deletions(-) delete mode 100644 docs/source/code_examples/functional/rosette.diff diff --git a/Makefile b/Makefile index 6f5fec6a2..73dd2e335 100644 --- a/Makefile +++ b/Makefile @@ -996,6 +996,18 @@ docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' +docs/source/generated/%.cc: backends/%.cc + $(Q) mkdir -p $(@D) + $(Q) cp $< $@ + +# diff returns exit code 1 if the files are different, but it's not an error +docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc backends/functional/smtlib_rosette.cc + $(Q) mkdir -p $(@D) + $(Q) diff -U 20 $^ > $@ || exit 0 + +PHONY: docs/gen/functional_ir +docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff + PHONY: docs/gen docs/usage docs/reqs docs/gen: $(TARGETS) $(Q) $(MAKE) -C docs gen @@ -1031,7 +1043,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage +docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage docs/gen/functional_ir DOC_TARGET ?= html docs: docs/prep diff --git a/docs/source/code_examples/functional/rosette.diff b/docs/source/code_examples/functional/rosette.diff deleted file mode 100644 index 35c7936d1..000000000 --- a/docs/source/code_examples/functional/rosette.diff +++ /dev/null @@ -1,372 +0,0 @@ -diff --git a/backends/functional/smtlib.cc b/backends/functional/smtlib_rosette.cc -index 3eacf407c..c9e737d19 100644 ---- a/backends/functional/smtlib.cc -+++ b/backends/functional/smtlib_rosette.cc -@@ -29,80 +29,86 @@ PRIVATE_NAMESPACE_BEGIN - using SExprUtil::list; - - const char *reserved_keywords[] = { -- // reserved keywords from the smtlib spec -- "BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par", -- "assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes", -- "declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort", -- "exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model", -- "get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value", -- "pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option", -+ // reserved keywords from the racket spec -+ "struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write", -+ "stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply", -+ "if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync", -+ "future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp", -+ "connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes", -+ "flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate", -+ "define-generics", "set", - - // reserved for our own purposes -- "pair", "Pair", "first", "second", -- "inputs", "state", -+ "inputs", "state", "name", - nullptr - }; - --struct SmtScope : public Functional::Scope { -- SmtScope() { -+struct SmtrScope : public Functional::Scope { -+ SmtrScope() { - for(const char **p = reserved_keywords; *p != nullptr; p++) - reserve(*p); - } - bool is_character_legal(char c, int index) override { -- return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c)); -+ return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); - } - }; - --struct SmtSort { -+struct SmtrSort { - Functional::Sort sort; -- SmtSort(Functional::Sort sort) : sort(sort) {} -+ SmtrSort(Functional::Sort sort) : sort(sort) {} - SExpr to_sexpr() const { - if(sort.is_memory()) { -- return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width())); -+ return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width())); - } else if(sort.is_signal()) { -- return list("_", "BitVec", sort.width()); -+ return list("bitvector", sort.width()); - } else { - log_error("unknown sort"); - } - } - }; - --class SmtStruct { -+class SmtrStruct { - struct Field { -- SmtSort sort; -+ SmtrSort sort; - std::string accessor; -+ std::string name; - }; - idict field_names; - vector fields; -- SmtScope &scope; -+ SmtrScope &global_scope; -+ SmtrScope local_scope; - public: - std::string name; -- SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {} -- void insert(IdString field_name, SmtSort sort) { -+ SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} -+ void insert(IdString field_name, SmtrSort sort) { - field_names(field_name); -- auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name)); -- fields.emplace_back(Field{sort, accessor}); -+ auto base_name = local_scope.unique_name(field_name); -+ auto accessor = name + "-" + base_name; -+ global_scope.reserve(accessor); -+ fields.emplace_back(Field{sort, accessor, base_name}); - } - void write_definition(SExprWriter &w) { -- w.open(list("declare-datatype", name)); -- w.open(list()); -- w.open(list(name)); -- for(const auto &field : fields) -- w << list(field.accessor, field.sort.to_sexpr()); -- w.close(3); -+ vector field_list; -+ for(const auto &field : fields) { -+ field_list.emplace_back(field.name); -+ } -+ w.push(); -+ w.open(list("struct", name, field_list, "#:transparent")); -+ if (field_names.size()) { -+ for (const auto &field : fields) { -+ auto bv_type = field.sort.to_sexpr(); -+ w.comment(field.name + " " + bv_type.to_string()); -+ } -+ } -+ w.pop(); - } - template void write_value(SExprWriter &w, Fn fn) { -- if(field_names.empty()) { -- // Zero-argument constructors in SMTLIB must not be called as functions. -- w << name; -- } else { -- w.open(list(name)); -- for(auto field_name : field_names) { -- w << fn(field_name); -- w.comment(RTLIL::unescape_id(field_name), true); -- } -- w.close(); -+ w.open(list(name)); -+ for(auto field_name : field_names) { -+ w << fn(field_name); -+ w.comment(RTLIL::unescape_id(field_name), true); - } -+ w.close(); - } - SExpr access(SExpr record, IdString name) { - size_t i = field_names.at(name); -@@ -117,28 +123,28 @@ std::string smt_const(RTLIL::Const const &c) { - return s; - } - --struct SmtPrintVisitor : public Functional::AbstractVisitor { -+struct SmtrPrintVisitor : public Functional::AbstractVisitor { - using Node = Functional::Node; - std::function n; -- SmtStruct &input_struct; -- SmtStruct &state_struct; -+ SmtrStruct &input_struct; -+ SmtrStruct &state_struct; - -- SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} -+ SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} - - SExpr from_bool(SExpr &&arg) { -- return list("ite", std::move(arg), "#b1", "#b0"); -+ return list("bool->bitvector", std::move(arg)); - } - SExpr to_bool(SExpr &&arg) { -- return list("=", std::move(arg), "#b1"); -+ return list("bitvector->bool", std::move(arg)); - } -- SExpr extract(SExpr &&arg, int offset, int out_width = 1) { -- return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); -+ SExpr to_list(SExpr &&arg) { -+ return list("bitvector->bits", std::move(arg)); - } - - 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 slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); } -+ SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); } -+ SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); } - 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)); } -@@ -150,16 +156,11 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { - 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), smt_const(RTLIL::Const(State::S1, a.width())))); } -- SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(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) 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 reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); } -+ SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); } -+ SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); } -+ SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); } -+ SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", 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))); } -@@ -167,32 +168,32 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { - - SExpr extend(SExpr &&a, int in_width, int out_width) { - if(in_width < out_width) -- return list(list("_", "zero_extend", out_width - in_width), std::move(a)); -+ return list("zero-extend", std::move(a), list("bitvector", out_width)); - else - return std::move(a); - } - 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 constant(Node, RTLIL::Const const &value) override { return smt_const(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 mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); } -+ SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); } -+ SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); } -+ SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); } - - SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } - SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } - }; - --struct SmtModule { -+struct SmtrModule { - Functional::IR ir; -- SmtScope scope; -+ SmtrScope scope; - std::string name; - -- SmtStruct input_struct; -- SmtStruct output_struct; -- SmtStruct state_struct; -+ SmtrStruct input_struct; -+ SmtrStruct output_struct; -+ SmtrStruct state_struct; - -- SmtModule(Module *module) -+ SmtrModule(Module *module) - : ir(Functional::IR::from_module(module)) - , scope() - , name(scope.unique_name(module->name)) -@@ -200,7 +201,7 @@ struct SmtModule { - , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) - , state_struct(scope.unique_name(module->name.str() + "_State"), scope) - { -- scope.reserve(name + "-initial"); -+ scope.reserve(name + "_initial"); - for (auto input : ir.inputs()) - input_struct.insert(input->name, input->sort); - for (auto output : ir.outputs()) -@@ -212,14 +213,11 @@ struct SmtModule { - void write_eval(SExprWriter &w) - { - w.push(); -- w.open(list("define-fun", name, -- list(list("inputs", input_struct.name), -- list("state", state_struct.name)), -- list("Pair", output_struct.name, state_struct.name))); -+ w.open(list("define", list(name, "inputs", "state"))); - auto inlined = [&](Functional::Node n) { - return n.fn() == Functional::Fn::constant; - }; -- SmtPrintVisitor visitor(input_struct, state_struct); -+ SmtrPrintVisitor visitor(input_struct, state_struct); - auto node_to_sexpr = [&](Functional::Node n) -> SExpr { - if(inlined(n)) - return n.visit(visitor); -@@ -230,9 +228,9 @@ struct SmtModule { - for(auto n : ir) - if(!inlined(n)) { - w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); -- w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true); -+ w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true); - } -- w.open(list("pair")); -+ w.open(list("cons")); - output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); - state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); - w.pop(); -@@ -240,19 +238,23 @@ struct SmtModule { - - void write_initial(SExprWriter &w) - { -- std::string initial = name + "-initial"; -- w << list("declare-const", initial, state_struct.name); -+ w.push(); -+ auto initial = name + "_initial"; -+ w.open(list("define", initial)); -+ w.open(list(state_struct.name)); - for (auto state : ir.states()) { -- if(state->sort.is_signal()) -- w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal()))); -- else if(state->sort.is_memory()) { -+ if (state->sort.is_signal()) -+ w << list("bv", smt_const(state->initial_value_signal()), state->sort.width()); -+ else if (state->sort.is_memory()) { - const auto &contents = state->initial_value_memory(); -+ w.open(list("list")); - for(int i = 0; i < 1<sort.addr_width(); i++) { -- auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width())); -- w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i]))); -+ w << list("bv", smt_const(contents[i]), state->sort.data_width()); - } -+ w.close(); - } - } -+ w.pop(); - } - - void write(std::ostream &out) -@@ -263,33 +265,53 @@ struct SmtModule { - output_struct.write_definition(w); - state_struct.write_definition(w); - -- w << list("declare-datatypes", -- list(list("Pair", 2)), -- list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); -- - write_eval(w); - write_initial(w); - } - }; - --struct FunctionalSmtBackend : public Backend { -- FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {} -+struct FunctionalSmtrBackend : public Backend { -+ FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {} - -- void help() override { log("\nFunctional SMT Backend.\n\n"); } -+ void help() override { -+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| -+ log("\n"); -+ log(" write_functional_rosette [options] [filename]\n"); -+ log("\n"); -+ log("Functional Rosette Backend.\n"); -+ log("\n"); -+ log(" -provides\n"); -+ log(" include 'provide' statement(s) for loading output as a module\n"); -+ log("\n"); -+ } - - void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override - { -- log_header(design, "Executing Functional SMT Backend.\n"); -+ auto provides = false; -+ -+ log_header(design, "Executing Functional Rosette Backend.\n"); - -- size_t argidx = 1; -- extra_args(f, filename, args, argidx, design); -+ size_t argidx; -+ for (argidx = 1; argidx < args.size(); argidx++) -+ { -+ if (args[argidx] == "-provides") -+ provides = true; -+ else -+ break; -+ } -+ extra_args(f, filename, args, argidx); -+ -+ *f << "#lang rosette/safe\n"; -+ if (provides) { -+ *f << "(provide (all-defined-out))\n"; -+ } - - for (auto module : design->selected_modules()) { - log("Processing module `%s`.\n", module->name.c_str()); -- SmtModule smt(module); -- smt.write(*f); -+ SmtrModule smtr(module); -+ smtr.write(*f); - } - } --} FunctionalSmtBackend; -+} FunctionalSmtrBackend; - - PRIVATE_NAMESPACE_END diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 81d987f0e..42eabc0b1 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -147,9 +147,9 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr Example: Adapting SMT-LIB backend for Rosette --------------------------------------------- -This section will walk through the process of adapting the SMT-LIB functional -backend (`write_functional_smt2`) to work with another s-expression target, -`Rosette`_ (`write_functional_rosette`). +This section will introduce the SMT-LIB functional backend +(`write_functional_smt2`) and what changes are needed to work with another +s-expression target, `Rosette`_ (`write_functional_rosette`). .. _Rosette: http://emina.github.io/rosette/ @@ -177,12 +177,19 @@ Scope ~~~~~ As described above, the ``Functional::Scope`` class is derived in order to avoid -collisions between identifiers in the generated output. We switch out ``Smt`` -in the class name for ``Smtr`` for ``smtlib_rosette``; which will happen through -the rest of the code too. We also update the ``is_character_legal`` method to -reject ascii characters which are not allowed in Racket variable names. +collisions between identifiers in the generated output. In the SMT-LIB version +the ``SmtScope`` class implements ``Scope``; provides a constructor that +iterates over a list of reserved keywords, calling ``reserve`` on each; and +defines the ``is_character_legal`` method to reject any characters which are not +allowed in SMT-LIB variable names to then be replaced with underscores in the +output. -.. literalinclude:: /code_examples/functional/rosette.diff +In the Rosette version we switch out ``Smt`` in the class name for ``Smtr`` to +mean ``smtlib_rosette``; this will happen through the rest of the code too. We +also update list of legal ascii characters in the ``is_character_legal`` method +to only those allowed in Racket variable names. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Scope`` class :start-at: -struct SmtScope : public Functional::Scope { @@ -218,13 +225,13 @@ and racket specifications to save on space in this document. Sort ~~~~ -The ``Sort`` class is a wrapper for the ``Functional::Sort`` class, providing -the additional functionality of mapping variable declarations to s-expressions -with the ``to_sexpr()`` method. The main change from ``SmtSort`` to -``SmtrSort`` is a syntactical one with signals represented as ``bitvector``\ -s, and memories as ``list``\ s of signals. +Next up in `write_functional_smt2` we see the ``Sort`` class. This is a wrapper +for the ``Functional::Sort`` class, providing the additional functionality of +mapping variable declarations to s-expressions with the ``to_sexpr()`` method. +The main change from ``SmtSort`` to ``SmtrSort`` is a syntactical one with +signals represented as ``bitvector``\ s, and memories as ``list``\ s of signals. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Sort`` wrapper :start-at: SExpr to_sexpr() const { @@ -233,37 +240,56 @@ s, and memories as ``list``\ s of signals. Struct ~~~~~~ -The SMT-LIB backend uses a class, ``SmtStruct``, to help with describing the -input, output, and state data structs. Where each struct in the SMT-LIB output -is a new ``datatype`` with each element having its type declared using the -`Sort`_ above, in Rosette we use the native ``struct`` with each field only -being referred to by name. For ease of use, we include comments for each field -to indicate the expected type. +The ``Functional::IR`` class tracks the set of inputs, the set of outputs, and +the set of "state" variables. The SMT-LIB backend maps each of these sets into +its own ``SmtStruct``, with each variable getting a corresponding field in the +struct and a specified `Sort`_. `write_functional_smt2` then defines each of +these structs as a new ``datatype``, with each element being strongly-typed. -.. literalinclude:: /code_examples/functional/rosette.diff +In Rosette, rather than defining new datatypes for our structs, we use the +native ``struct``. We also only declare each field by name because Racket is +not as strongly-typed. For ease of use, we provide the expected type for each +field as comments. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``write_definition`` method :start-at: void write_definition :end-before: template void write_value -Struct fields in Rosette are accessed as ``-``, where -field names only need to be unique within the struct, while accessors are unique -within the module. We thus modify the class constructor and ``insert`` method -to support this; providing one scope that is local to the struct -(``local_scope``) and one which is shared across the whole module -(``global_scope``). +Each field is added to the ``SmtStruct`` with the ``insert`` method, which also +reserves a unique name (or accessor) within the `Scope`_. These accessors +combine the struct name and field name and are globally unique, being used in +the ``access`` method for reading values from the input/current state. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/smtlib.cc + :language: c++ + :caption: ``Struct::access()`` method + :start-at: SExpr access( + :end-before: }; + +In Rosette, struct fields are accessed as ``-`` so +including the struct name in the field name would be redundant. For +`write_functional_rosette` we instead choose to make field names unique only +within the struct, while accessors are unique across the whole module. We thus +modify the class constructor and ``insert`` method to support this; providing +one scope that is local to the struct (``local_scope``) and one which is shared +across the whole module (``global_scope``), leaving the ``access`` method +unchanged. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of struct constructor :start-at: SmtStruct(std::string name, SmtScope &scope) :end-before: void write_definition -For writing outputs/next state (the ``write_value`` method), the only change is -to remove the check for zero-argument constructors since this is not necessary -with Rosette ``struct``\ s. +Finally, ``SmtStruct`` also provides a ``write_value`` template method which +calls a provided function on each element in the struct. This is used later for +assigning values to the output/next state pair. The only change here is to +remove the check for zero-argument constructors since this is not necessary with +Rosette ``struct``\ s. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``write_value`` method :start-at: template void write_value @@ -273,11 +299,13 @@ PrintVisitor ~~~~~~~~~~~~ The ``PrintVisitor`` implements the abstract ``Functional::AbstractVisitor`` -class for converting FunctionalIR functions into s-expressions, including -reading inputs/current state. For most functions, the Rosette output is very -similar to the corresponding SMT-LIB function with minor adjustments for syntax. +class, described above in `What is FunctionalIR`_, with a return type of +``SExpr``. This class converts FunctionalIR functions into s-expressions, +including reading inputs/current state with the ``access`` method from the +`Struct`_. For most functions, the Rosette output is very similar to the +corresponding SMT-LIB function with minor adjustments for syntax. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities :start-at: SExpr logical_shift_left @@ -286,7 +314,7 @@ similar to the corresponding SMT-LIB function with minor adjustments for syntax. However there are some differences in the two formats with regards to how booleans are handled, with Rosette providing built-in functions for conversion. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing differences :start-at: SExpr from_bool @@ -295,14 +323,21 @@ booleans are handled, with Rosette providing built-in functions for conversion. Module ~~~~~~ -The ``Functional::IR`` is wrapped in the ``Module`` class, with the mapping from -RTLIL module to FunctionalIR happening in the constructor. Each of the three -structs; inputs, outputs, and state; are then created from the corresponding -lists in the IR. The only change here is rename the initial state to use ``_`` -instead of ``-``, since the ``-`` in Rosette can be used to access struct -fields. +The ``Functional::IR`` is wrapped in the ``SmtModule`` class, with the mapping +from RTLIL module to FunctionalIR happening in the constructor. Each of the +three ``SmtStruct``\ s; inputs, outputs, and state; are created, with each value +in the corresponding lists in the IR being ``insert``\ ed. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/smtlib.cc + :language: c++ + :caption: ``SmtModule`` constructor + :start-at: SmtModule(Module + :end-at: } + +Since Racket uses the ``-`` to access struct fields, the ``SmtrModule`` instead +uses an underscore for the name of the initial state. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module`` constructor :start-at: scope.reserve(name @@ -316,25 +351,11 @@ current_state) -> (outputs, next_state)`` with ``write_eval``, and declaring the initial state with ``write_initial``. The only change for the ``SmtrModule`` is that the ``pair`` declaration isn't needed. -.. inlined diff to show the complete function -.. code-block:: diff +.. literalinclude:: /generated/functional/rosette.diff + :language: diff :caption: diff of ``Module::write()`` method - - void write(std::ostream &out) - { - SExprWriter w(out); - - input_struct.write_definition(w); - output_struct.write_definition(w); - state_struct.write_definition(w); - - - w << list("declare-datatypes", - - list(list("Pair", 2)), - - list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); - - - write_eval(w); - write_initial(w); - } + :start-at: void write(std::ostream &out) + :end-at: } For the ``write_eval`` method, the main differences are syntactical. First we change the function declaration line for the Rosette style which drops the @@ -371,7 +392,7 @@ variable, ``[name]_initial``, can then be used in the ``[name]`` function call; allowing the Rosette code to be used in the generation of the ``next_state``, whereas the SMT-LIB code can only verify that a given ``next_state`` is correct. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module::write_initial()`` method :start-at: void write_initial @@ -396,7 +417,7 @@ an additional line declaring that everything in the file should be exported for use; allowing the file to be treated as a Racket package with structs and mapping function available for use externally. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: relevant portion of diff of ``Backend::execute()`` method :start-at: lang rosette/safe