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).
This commit is contained in:
Krystine Sherwin 2025-02-22 17:14:13 +13:00
parent 819c3260ec
commit c429aef60f
No known key found for this signature in database
3 changed files with 98 additions and 437 deletions

View File

@ -996,6 +996,18 @@ docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS)
docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS)
$(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' $(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 PHONY: docs/gen docs/usage docs/reqs
docs/gen: $(TARGETS) docs/gen: $(TARGETS)
$(Q) $(MAKE) -C docs gen $(Q) $(MAKE) -C docs gen
@ -1031,7 +1043,7 @@ docs/reqs:
$(Q) $(MAKE) -C docs reqs $(Q) $(MAKE) -C docs reqs
.PHONY: docs/prep .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 DOC_TARGET ?= html
docs: docs/prep docs: docs/prep

View File

@ -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<int> {
- SmtScope() {
+struct SmtrScope : public Functional::Scope<int> {
+ 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<IdString> field_names;
vector<Field> 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<SExpr> 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<typename Fn> 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<SExpr> {
+struct SmtrPrintVisitor : public Functional::AbstractVisitor<SExpr> {
using Node = Functional::Node;
std::function<SExpr(Node)> 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> {
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<SExpr> 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> {
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<<state->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<std::string> 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

View File

@ -147,9 +147,9 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr
Example: Adapting SMT-LIB backend for Rosette Example: Adapting SMT-LIB backend for Rosette
--------------------------------------------- ---------------------------------------------
This section will walk through the process of adapting the SMT-LIB functional This section will introduce the SMT-LIB functional backend
backend (`write_functional_smt2`) to work with another s-expression target, (`write_functional_smt2`) and what changes are needed to work with another
`Rosette`_ (`write_functional_rosette`). s-expression target, `Rosette`_ (`write_functional_rosette`).
.. _Rosette: http://emina.github.io/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 As described above, the ``Functional::Scope`` class is derived in order to avoid
collisions between identifiers in the generated output. We switch out ``Smt`` collisions between identifiers in the generated output. In the SMT-LIB version
in the class name for ``Smtr`` for ``smtlib_rosette``; which will happen through the ``SmtScope`` class implements ``Scope<int>``; provides a constructor that
the rest of the code too. We also update the ``is_character_legal`` method to iterates over a list of reserved keywords, calling ``reserve`` on each; and
reject ascii characters which are not allowed in Racket variable names. 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 :language: diff
:caption: diff of ``Scope`` class :caption: diff of ``Scope`` class
:start-at: -struct SmtScope : public Functional::Scope<int> { :start-at: -struct SmtScope : public Functional::Scope<int> {
@ -218,13 +225,13 @@ and racket specifications to save on space in this document.
Sort Sort
~~~~ ~~~~
The ``Sort`` class is a wrapper for the ``Functional::Sort`` class, providing Next up in `write_functional_smt2` we see the ``Sort`` class. This is a wrapper
the additional functionality of mapping variable declarations to s-expressions for the ``Functional::Sort`` class, providing the additional functionality of
with the ``to_sexpr()`` method. The main change from ``SmtSort`` to mapping variable declarations to s-expressions with the ``to_sexpr()`` method.
``SmtrSort`` is a syntactical one with signals represented as ``bitvector``\ The main change from ``SmtSort`` to ``SmtrSort`` is a syntactical one with
s, and memories as ``list``\ s of signals. 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 :language: diff
:caption: diff of ``Sort`` wrapper :caption: diff of ``Sort`` wrapper
:start-at: SExpr to_sexpr() const { :start-at: SExpr to_sexpr() const {
@ -233,37 +240,56 @@ s, and memories as ``list``\ s of signals.
Struct Struct
~~~~~~ ~~~~~~
The SMT-LIB backend uses a class, ``SmtStruct``, to help with describing the The ``Functional::IR`` class tracks the set of inputs, the set of outputs, and
input, output, and state data structs. Where each struct in the SMT-LIB output the set of "state" variables. The SMT-LIB backend maps each of these sets into
is a new ``datatype`` with each element having its type declared using the its own ``SmtStruct``, with each variable getting a corresponding field in the
`Sort`_ above, in Rosette we use the native ``struct`` with each field only struct and a specified `Sort`_. `write_functional_smt2` then defines each of
being referred to by name. For ease of use, we include comments for each field these structs as a new ``datatype``, with each element being strongly-typed.
to indicate the expected type.
.. 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 :language: diff
:caption: diff of ``write_definition`` method :caption: diff of ``write_definition`` method
:start-at: void write_definition :start-at: void write_definition
:end-before: template<typename Fn> void write_value :end-before: template<typename Fn> void write_value
Struct fields in Rosette are accessed as ``<struct_name>-<field_name>``, where Each field is added to the ``SmtStruct`` with the ``insert`` method, which also
field names only need to be unique within the struct, while accessors are unique reserves a unique name (or accessor) within the `Scope`_. These accessors
within the module. We thus modify the class constructor and ``insert`` method combine the struct name and field name and are globally unique, being used in
to support this; providing one scope that is local to the struct the ``access`` method for reading values from the input/current state.
(``local_scope``) and one which is shared across the whole module
(``global_scope``).
.. 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 ``<struct_name>-<field_name>`` 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 :language: diff
:caption: diff of struct constructor :caption: diff of struct constructor
:start-at: SmtStruct(std::string name, SmtScope &scope) :start-at: SmtStruct(std::string name, SmtScope &scope)
:end-before: void write_definition :end-before: void write_definition
For writing outputs/next state (the ``write_value`` method), the only change is Finally, ``SmtStruct`` also provides a ``write_value`` template method which
to remove the check for zero-argument constructors since this is not necessary calls a provided function on each element in the struct. This is used later for
with Rosette ``struct``\ s. 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 :language: diff
:caption: diff of ``write_value`` method :caption: diff of ``write_value`` method
:start-at: template<typename Fn> void write_value :start-at: template<typename Fn> void write_value
@ -273,11 +299,13 @@ PrintVisitor
~~~~~~~~~~~~ ~~~~~~~~~~~~
The ``PrintVisitor`` implements the abstract ``Functional::AbstractVisitor`` The ``PrintVisitor`` implements the abstract ``Functional::AbstractVisitor``
class for converting FunctionalIR functions into s-expressions, including class, described above in `What is FunctionalIR`_, with a return type of
reading inputs/current state. For most functions, the Rosette output is very ``SExpr``. This class converts FunctionalIR functions into s-expressions,
similar to the corresponding SMT-LIB function with minor adjustments for syntax. 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 :language: diff
:caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities
:start-at: SExpr logical_shift_left :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 However there are some differences in the two formats with regards to how
booleans are handled, with Rosette providing built-in functions for conversion. booleans are handled, with Rosette providing built-in functions for conversion.
.. literalinclude:: /code_examples/functional/rosette.diff .. literalinclude:: /generated/functional/rosette.diff
:language: diff :language: diff
:caption: portion of ``Functional::AbstractVisitor`` implementation diff showing differences :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing differences
:start-at: SExpr from_bool :start-at: SExpr from_bool
@ -295,14 +323,21 @@ booleans are handled, with Rosette providing built-in functions for conversion.
Module Module
~~~~~~ ~~~~~~
The ``Functional::IR`` is wrapped in the ``Module`` class, with the mapping from The ``Functional::IR`` is wrapped in the ``SmtModule`` class, with the mapping
RTLIL module to FunctionalIR happening in the constructor. Each of the three from RTLIL module to FunctionalIR happening in the constructor. Each of the
structs; inputs, outputs, and state; are then created from the corresponding three ``SmtStruct``\ s; inputs, outputs, and state; are created, with each value
lists in the IR. The only change here is rename the initial state to use ``_`` in the corresponding lists in the IR being ``insert``\ ed.
instead of ``-``, since the ``-`` in Rosette can be used to access struct
fields.
.. 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 :language: diff
:caption: diff of ``Module`` constructor :caption: diff of ``Module`` constructor
:start-at: scope.reserve(name :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 initial state with ``write_initial``. The only change for the ``SmtrModule`` is
that the ``pair`` declaration isn't needed. that the ``pair`` declaration isn't needed.
.. inlined diff to show the complete function .. literalinclude:: /generated/functional/rosette.diff
.. code-block:: diff :language: diff
:caption: diff of ``Module::write()`` method :caption: diff of ``Module::write()`` method
:start-at: void write(std::ostream &out)
void write(std::ostream &out) :end-at: }
{
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);
}
For the ``write_eval`` method, the main differences are syntactical. First we For the ``write_eval`` method, the main differences are syntactical. First we
change the function declaration line for the Rosette style which drops the 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``, 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. 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 :language: diff
:caption: diff of ``Module::write_initial()`` method :caption: diff of ``Module::write_initial()`` method
:start-at: void write_initial :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 use; allowing the file to be treated as a Racket package with structs and
mapping function available for use externally. mapping function available for use externally.
.. literalinclude:: /code_examples/functional/rosette.diff .. literalinclude:: /generated/functional/rosette.diff
:language: diff :language: diff
:caption: relevant portion of diff of ``Backend::execute()`` method :caption: relevant portion of diff of ``Backend::execute()`` method
:start-at: lang rosette/safe :start-at: lang rosette/safe