Contributing
Thank you for considering contributing to Spade! This chapter contains some details on various things that are useful to know when contributing. When you inevitably have questions, reach out on Discord community or in the Matrix channel. (These are bridged, pick your favorite platform.)
Repositories
We are currently in the process of moving from gitlab.com to codeberg. For a while, the compiler and swim build tool will remain on GitLab (https://gitlab.com/spade-lang/), but many other projects have been migrated to (https://codeberg.org/spade-lang/).
What to Work On?
We are open to contributions of any kind: improvements and bug fixes to the compiler and build system of course, but also documentations, new functions in the standard library or even new cool libraries for the ecosystem.
In the compiler and build system, we have a good first issue label which we apply to issues which we think are solvable by someone new to the project. We are also more than happy to guide you through the implementation, just leave a comment in the issue or in the chat channels!
Writing tests
Most parts of the Spade compiler can be tested with one of three macros:
code_compiles!snapshot_error!snapshot_mir!
You can get examples of how they work in the spade-tests crate where you should write most tests. All three macros take a test name and a snippet of source code which should be tested. code_compiles simply tests to make sure that there are no compile errors when compiling the snippet.
The other two are snapshot tests, meaning that they expect some output from the compiler, but the expected output is not manually written. Instead, every time the test is run, its output is compared to the previous time the test was run. If there is a difference, you can accept or reject that change. snapshot_error is used to ensure that error messages are helpful and don’t change, and is also the preferred way to ensure that errors are emitted when they should be. While snapshot_error could be used as a code_compiles macro by simply accepting no error message as the correct output, it is better to communicate the intent of the test by using code_compiles
snapshot_mir is a snapshot error on the output of the compiler. Or rather, on the MIR output which is the final step emitted before Verilog. Use this when the goal is to test that the output of some new construct is correct. However, because the snapshot_mir tests often have variable names get changed, they should only be used when the output is likely to be relevant. For example, don’t use snapshot_mir to make sure that a new type inference feature doesn’t trigger an error, instead use code_compiles
Reviewing snapshots
To run the test suite, simply cargo test. If any snapshot tests fail, it will tell you, and you now have to review the changes. This is done with a tool called insta https://insta.rs/docs/cli/. Install it, and then run
cargo insta review
to go through the failed tests and accept or reject the changes.
If only a few tests failed and the results all look good, you can run
cargo insta accept
to accept everything, or
cargo insta reject
to reject all the failures.
Swim Tests / Testing the Generated Code
If you want to test your changes in a real project and run Verilog simulation on the generated code, you can do so by adding a test to the swim_tests project in the Spade repo. This requires adding the code to test to src/, and a python testbench to the tests/ directory. Then you can simply run swim test to run all the tests.
Testing Your Changes During Development
You can run the Spade compiler directly on a test file with
cargo run --bin spade -- -o /tmp/test.sv test.spade --omit-stdlib
However, if you have just a small single file test, it is better to use one of the test macros as that gives a test for your feature for free once you’re done!
If you want to use your new compiler in a project for testing, there are a few ways. First, you can edit swim.toml to set
compiler.git = "<your git repo URL>"
compiler.branch = "<your branch name>"
then run swim update-spade
This is useful if you have a project that needs your feature but it hasn’t been merged yet, but has the downside of requiring a push for every change.
For testing locally, you can run
swim --override-compiler <path to your compiler> <your normal swim command>
Or, you can set the path to the compiler in your project
compiler.path = "<path to your compiler>"
Figuring Out What is Going On
If you are chasing a bug, there are a few tools that can help you out. First, for figuring out parser or type inference issues, you can set
SPADE_TRACE_PARSER=1
and
SPADE_TRACE_TYPEINFERENCE=<path to a unit>
which will print a formatted trace of what the steps were doing.
For debugging swim, you can set SWIM_LOG=<level> where level is debug, trace, info, warn, or error to get more information about what it is doing.
If you experience a crash or freeze of the compiler, it can be useful to run it through gdb. For that, you can use
swim --debug-spadec <your normal swim command>
If you want to run the compiler through another analysis tool such as flamegraph for profiling, you can use
swim --wrap-spadec <your wrapper program> --wrapper-args <arg1> --wrapper-args <arg2> ... <your normal swim command>
which will call <your wrapper program> <arg1> <arg2> spadec instead of just spadec
Here it can also be useful to add --spadec-debug and/or --rustflags in order to get things like debug symbols.
AI Policy / LLM contributions
Contributions must not include content generated by large language models or other probabilistic tools, including but not limited to Copilot or ChatGPT. This policy covers code, documentation, pull requests, issues, comments, and any other contributions to the Spade project.
For now, we’re taking a cautious approach to these tools due to their effects — both unknown and observed — on project health and maintenance burden. This field is evolving quickly, so we are open to revising this policy at a later date, given proposals for particular tools that mitigate these effects. Our rationale is as follows:
Maintainer burden: Reviewers depend on contributors to write and test their code before submitting it. We have found that these tools make it easy to generate large amounts of plausible-looking code that the contributor does not understand, is often untested, and does not function properly. This is a drain on the (already limited) time and energy of our reviewers. In addition, reviewing a human contributed contribution not only brings N lines of code into the project, but helps mentor a potential future maintainer. An LLM coded contribution takes the same amount of effort to review, but only brings in the additional N lines of code.
Copyright issues: Publicly available models are trained on copyrighted content, both accidentally and intentionally, and their output often includes that content verbatim. Since the legality of this is uncertain, these contributions may violate the licenses of copyrighted works.
Ethical issues: LLMs require an unreasonable amount of energy to build and operate, their models are built with heavily exploited workers in unacceptable working conditions, and they are being used to undermine labor and justify layoffs. These are harms that we do not want to perpetuate, even if only indirectly.
Correctness: Even when code generated by LLMs does seem to function, there is no guarantee that it is correct. While both humans and AI can and will make mistakes, when a human makes a mistake there is someone available to ask about why a decision was made, and how code can be adapted to fix the issue in a way that does not cause future issues. With AI, there is no such opportunity.