7 releases (1 stable)
1.0.6 | Jun 27, 2020 |
---|---|
1.0.6-alpha5 | Sep 21, 2020 |
1.0.6-alpha3 | Sep 20, 2020 |
1.0.6-alpha2 | Jul 1, 2020 |
#100 in Simulation
5.5MB
42K
SLoC
lc3tools-sys
Rust bindings for LC3Tools
.
Usage
Add this to your Cargo.toml
:
[dependencies]
lc3tools-sys = "1.0.6"
Since the bindings this crate exposes are exactly one to one with the LC3Tools
API, the LC3Tools
source code and documentation are the best place to go for information about how to use this crate, especially the API documentation.
Headers
Headers are exposed at the path the DEP_LC3CORE_LINKS
env var points to.
Caveats
However, note that LC3Tools
exposes a C++ API. Though bindings for it are provided in this crate, it's extremely unlikely they will work with your OS/platform/compiler/compiler flags. Different platforms seem to have different name-mangling conventions and layout isn't (afaik) stable across different configurations (switching from -O3
to -O0
breaks the C++ interface example with my configuration, for example).
All of this (mangled symbols, layout information) is encoded in the generated bindings. Note that because linkers are often lazy, even though the symbols in the generated bindings don't match those that are actually produced on your platform, you may not get a compile error unless/until you actually go to use (transitively) those symbols in your final binary. This is why the C++ interface example is feature gated.
We offer a generate-fresh
feature so that you can generate this file locally at build time, but it still remains unlikely that the C++ interface will work/be of use. Things like vtables are represented by opaque types and even if you manage to get a hold of a C++ generated vtable to pass along sometimes things still don't work.
For example, for reasons still unknown, when running the C++ interface example, the printer
that's passed to the simulator mysteriously turns into a NULL
but only in the copy of the logger that's given to the state
instance; the copy that's in the logger
remains unchanged. I was only able to get the example to work after making the following changes to LC3Tools
:
Click to show the diff.
diff --git a/backend/logger.h b/backend/logger.h
index b7146ac..c172acb 100644
--- a/backend/logger.h
+++ b/backend/logger.h
@@ -28,10 +28,17 @@ namespace utils
template<typename ... Args>
void printf(PrintType level, bool bold, std::string const & format, Args ... args) const;
void newline(PrintType level = PrintType::P_ERROR) const {
- if(static_cast<uint32_t>(level) <= print_level) { printer.newline(); }
}
void print(std::string const & str) {
- if(print_level > static_cast<uint32_t>(PrintType::P_NONE)) { printer.print(str); }
}
uint32_t getPrintLevel(void) const { return print_level; }
void setPrintLevel(uint32_t print_level) { this->print_level = print_level; }
diff --git a/backend/simulator.cpp b/backend/simulator.cpp
index c8004e8..bd7f1db 100644
--- a/backend/simulator.cpp
+++ b/backend/simulator.cpp
@@ -112,7 +112,7 @@ void Simulator::simulate(void)
collecting_input = true;
- inputter.beginInput();
if(threaded_input) {
input_thread = std::thread(&core::Simulator::inputThread, this);
}
@@ -125,7 +125,7 @@ void Simulator::simulate(void)
executeEventChain(events);
updateDevices();
if(! threaded_input) {
- collectInput();
}
checkAndSetupInterrupts();
}
@@ -139,7 +139,7 @@ void Simulator::simulate(void)
if(threaded_input && input_thread.joinable()) {
input_thread.join();
}
- inputter.endInput();
Workarounds
To make this crate at least somewhat usable, we offer a limited set of C bindings that are only really good for running whole programs.
This is incredibly clunky but it was good enough™ for our use case. If actual Rust bindings for LC3Tools
are a thing you need, cxx
is probably worth looking into. Since this crate exports the LC3Tools
headers you could depend on this crate and use it for it's cc
setup (ignoring the bindings it has).
Alternatively, if there are specific additions to the C bindings you need, PRs are very welcome!
Features
LC3Tools
functionality features
The backend part of LC3Tools
is always included. The frontend
feature includes the files in frontend/common
. The grader
feature (which requires the frontend
feature) includes the files in frontend/grader
but strips out the main
function in framework.cpp
.
These features are both enabled by default.
Other features
generate-fresh
By default, Rust bindings for LC3Tools
aren't generated anew when building this crate. Instead we bundle pre-generated bindings and use them, by default. We do this because generating the bindings is a little time consuming (takes about a minute — unless you care deeply about how long clean builds take, this is a non-issue), but more importantly because generating the bindings is somewhat system-specific. bindgen
walks through the system libc and C++ standard library headers as part of doing so and we maintain a list of types and things for it to skip that's very libc/system/OS specific.
You'll probably never need to, but if you find yourself wanting to generate these bindings yourself (i.e. because you modified some headers in LC3Tools
), then you can build with the generate-fresh
feature (build.rs
goes and passes the right instructions to cargo
so you can just leave the feature enabled — it'll only actually do the work when one of the headers/files in the build graph change).
lto
Unfortunately, we can't enable LTO by default as it requires some setup and somewhat specific tools to be used (see this commit and this commit for some context and this page for details about what setup is needed).
So, we offer an lto
feature that passes the compiler cc
invokes the necessary flag. When using this feature you'll also need to make sure that you pass rustc
the LTO linker plugin flag and instruct it to use an appropriate linker, as described here. For this specific crate the necessary flags exist here, but commented out.
The final thing you need to do when using the lto
feature is to make sure that the compiler that cc
ends up using will work with the LTO linker plugin. The table here offers some information on what version should work, but it's somewhat out of date; if the same version of clang
is used by cc
to build LC3Tools
and by rustc
to do the linking, things should work (provided it's a relatively modern version of clang — CI in this repo uses version 9, successfully).
Actually figuring out and changing which compiler cc
uses is tricker; on Linux based systems ensuring that the c++
alias points to the desired version seems to do the trick (update-alternatives
might be able to help you with this).
Examples
Right now we have one example that runs an LC-3 program that multiplies two unsigned numbers. As mentioned, it has a C++ interface part and a C interface part. By default the C++ part is disabled as it's unlikely it will work on your machine.
cargo run --example mul
should run the C interface part.
Minimum Supported Rust Version (MSRV)
This crate is currently guaranteed to compile on stable Rust 1.43 and newer. We offer no guarantees that this will remain true in future releases but do promise to always support (at minimum) the latest stable Rust version and to document changes to the MSRV in the changelog.
Contributing
PRs are (very) welcome! See CONTRIBUTING.md for details.