feat: add .include directive for multi-file assembly#109
feat: add .include directive for multi-file assembly#109marcelofeitoza wants to merge 3 commits intoblueshift-gg:masterfrom
Conversation
|
@marcelofeitoza this is awesome! @clairechingching plz accept, I'll use this |
|
this is a well-established pattern in gcc and i'm in favor. main thing is, it would be our first major divergence from solana toolchain behavior. might be worth creating an equivalent patch to the shitty toolchain nobody uses just to maintain compatibility. assembly macros would be another such change that would improve devex a lot. |
|
@marcelofeitoza In discussing with @clairechingching, we probably need some kind of solution to not need the |
@deanmlittle I tested and it already works without
~/include-demo $ sbpf build
⚡️ Building "include-demo"
✅ "include-demo" built successfully in 1.408ms!
~/include-demo $ sbpf test
🧪 Running tests
Finished `release` profile [optimized] target(s) in 0.15s
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.22s
Running unittests src/lib.rs (target/debug/deps/include_demo-78841665d7ad6f91)
running 1 test
[2026-03-05T02:42:49.851350000Z DEBUG solana_runtime::message_processor::stable_log] Program 78zVfKLgPEotVzdGk2h5hyWBoe9fiwSCWbu2k4dJjLmh invoke [1]
[2026-03-05T02:42:49.852540000Z DEBUG solana_runtime::message_processor::stable_log] Program log: include works
[2026-03-05T02:42:49.852567000Z DEBUG solana_runtime::message_processor::stable_log] Program log: math works
[2026-03-05T02:42:49.852617000Z DEBUG solana_runtime::message_processor::stable_log] Program 78zVfKLgPEotVzdGk2h5hyWBoe9fiwSCWbu2k4dJjLmh consumed 213 of 1400000 compute units
[2026-03-05T02:42:49.852629000Z DEBUG solana_runtime::message_processor::stable_log] Program 78zVfKLgPEotVzdGk2h5hyWBoe9fiwSCWbu2k4dJjLmh success
test tests::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
Doc-tests include_demo
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
✅ Tests completed successfully! |
|
need a little help here. hoping to come to an agreement for backwards compatibility |
58fd90b to
889efa4
Compare
When multiple includes define the same label in .rodata/.data (e.g. message),
the build would fail with "Duplicate label". Labels are now prefixed with
`{path}___` (e.g. log.s → log___message, instructions/transfer/format.s →
instructions_transfer_format___message), so they no longer collide.
Made-with: Cursor
9ae334a to
5583e2b
Compare
|
This commit adds automatic prefixing for data labels in included files. When multiple includes define the same label in |
@deanmlittle yes please! |
already have a PR prepared for in |
|
@marcelofeitoza will this PR allow for unit testing individual functions, once they are broken out into a separate file? Perhaps it's worth a broader discussion, but the ability to test subroutines, especially with coverage would make ASM development significantly more auditable and verifiable |
|
@alnoki With Just tried it and it works. Layout: Running And in the logs: So the pattern works, even though it wasn’t the original goal of this PR with adding
|
|
the goal of this PR is great and addresses a wanted feature! but the implementation is not okay. we recently added debug feature to sbpf and doing this by just simply expanding the source program would loose source file/line tracking for the included assembly files imo @deanmlittle what's your opinion on resolving duplicate labels across different files? personally i like the idea of adding prefix to the labels in callee programs |
When I said we should do something about the globl thing, I didn't actually mean we necessarily had to solve it here. I think there's two ways we can look at this.
Imo, |
^ this makes total sense to me! |
|
I agree with both directions actually, think they will fit together: Claire’s point: Move Dean’s point: Treat Proposed approach: Implement |
this isn't quite how assembly macros look. if we were to copy NASM style, for example, it'd look something like this: ; Aligns a pointer to the next 8 bytes
; Parameters:
; 1. r_input - register pointing to value that needs alignment
; 2. r_temp - temporary register to store current input value for equality check
%macro align_8 2
mov64 %2, %1
and64 %2, -8
jeq %1, %2, 1
add64 %1, 8
%endmacroWhich can then be called like this: align_8 r3, r4You can imagine how quickly multiple optimizations of common yet cumbersome functions like CPI would stack up to make writing ASM feel effortless. |
|
@clairechingching are you good to merge this too? |
Approve here is just concept ack. We still need to consider the implementation details and test it out with our greater tooling. See comment: #109 (comment) |
Got it, thanks! I'm excited for this feature, already using it at DASMAC-com/dropset-beta#20 |
# Background The assembly build relies on recent `sbpf` features: - [`--arch v3`](blueshift-gg/sbpf#98) — SBPFv3 architecture target - [Multi-file assembly via `.include`](blueshift-gg/sbpf#109) - [`--deploy-dir` flag](blueshift-gg/sbpf#103) # Changes 1. Add SBPF assembly source files for entrypoint, market registration, and error handling 2. Add Cargo workspace configuration with `mollusk-svm` and `pinocchio` dependencies 3. Extend `Algorithm` component with collapsible Shiki-highlighted assembly blocks via new `asm` prop 4. Rename component `src` prop to `tex` and anchor IDs from `algo-` to `algorithm-` 5. Update algorithm TeX to rename `data` → `insn`, `discriminator` → `discriminant`, and related error/length constants 6. Add `asm` build target to Makefile and update `.gitignore` for `deploy` and `target` directories 7. Improve `docs-dev` and `docs-prettier` targets to run `npm install` and open browser on serve 8. Escape erroneous `chktex` errors
…26) # Changes 1. Add `rustfmt` and `clippy` as local pre-commit hooks in `cfg/pre-commit/lint.yml`, using the system toolchain for fast execution 2. Add Rust toolchain (`dtolnay/rust-toolchain`) and shared dependency cache (`Swatinem/rust-cache`) to the lint CI workflow 3. Add `test.yml` workflow that builds assembly via `make asm` and runs `make test`, sharing the Rust dependency cache with lint 4. Add `.github/actions/install-sbpf` composite action that installs and caches the `sbpf` CLI, pinned to the [`feat/include-directive`](blueshift-gg/sbpf#109) branch for multi-file assembly support 5. Update `make clean` to also remove docs artifacts (`node_modules`, `.vitepress/cache`, `.vitepress/dist`) and the assembly deploy directory 6. Add `make lint` target and update `make all` to run `lint` and `test`

Add
.includedirective for multi-file assemblySummary
Adds support for
.include "path"in sBPF assembly, allowing programs to be split across multiple files. The directive is expanded at build time by inlining the referenced file's contents before assembly.Motivation
The sbpf CLI compiles a single
.sfile per program. Labels defined in other files (e.g.custom_log, shared utilities) are never resolved, causing "unsupported BPF instruction" or undefined symbol errors when trying to call across files. This change enables modular assembly projects without external tooling.Changes
src/commands/build.rs: Addedexpand_includes()that:.include "path"(path in double quotes)tests/utils.rs: Addedwrite_include_file()helper for integration teststests/test_include.rs: New integration tests:test_include_directive– main file includeslog.s, builds successfullytest_include_nested– nested includes (main → log.s → log_impl.s)test_include_missing_file_fails– missing include produces clear errorSyntax
.include)Failed to read include 'path': <io error>Example
main.s:
log.s:
Checklist
cargo fmt --allpassescargo clippy --all-targets --all-features -- -D warningspassescargo test --workspace --all-features --all-targetspasses