-
Notifications
You must be signed in to change notification settings - Fork 284
SIMD-0503: Static Sysvars #503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| --- | ||
| simd: '0503' | ||
| title: Static Sysvars | ||
| authors: | ||
| - Dean Little (Blueshift) | ||
| - Joe Caulfield (Anza) | ||
| category: Standard | ||
| type: Core | ||
| status: Idea | ||
| created: 2026-03-25 | ||
| feature: (fill in with feature key and github tracking issues once accepted) | ||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| Leverage existing static linking infrastructure of JIT compilation to enable | ||
| static resolution of sysvars. | ||
|
|
||
| ## Motivation | ||
|
|
||
| In order to access Sysvar values, developers currently have three options: | ||
|
|
||
| 1. Invoke a specific getter syscall | ||
| 2. Invoke the get_sysvar syscall, or | ||
| 3. Include a Sysvar account in their program. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obligatory https://xkcd.com/927/. That said, yes we could improve upon sysvars and the sysvar syscalls are not great. |
||
|
|
||
| The downsides of all of these approaches are threefold: | ||
|
|
||
| 1. Sysvar values are globals that are always available to the validator, but | ||
| aren't exposed as globals during execution. This is a clunky anti-pattern | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Scratch that, it only applies to the sysvar cache but it should be easy to expand to transaction accounts too. |
||
| resulting in degraded developer experience. | ||
| 2. Invoking a syscall to access a global requires both a stack allocation and | ||
| halting execution – an immense amount of overhead just to read a value that | ||
| is already readily available. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, sysvar syscalls are overkill for reading constant data. |
||
| 3. Having to pass in an account to access a global results in a 10kb penalty | ||
| to data serialization and has negative implications for composability. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additionally, depending on the design of the entrypoint a program must also find the instruction account of the sysvar, but that will be fixed by SIMD-0449.
That part will be gone after direct mapping and the CU charging adjustments of SIMD-0452, which I still have to rewrite.
I think that is the remaining actual downside of sysvar accounts: They waste key space in the instruction invocation. |
||
|
|
||
| If these globals were simply exposed to the VM, as is common in kernel BPF, | ||
| and resolved JIT, we could dramatically improve developer experience, whilst | ||
| also reducing the runtime overhead of accessing them to just 2 CUs. This is | ||
| generalizable to all Sysvars, however the majority of gains are realized by | ||
| simply implementing `Rent` and `Clock`. | ||
|
|
||
| ## New Terminology | ||
|
|
||
| Static Sysvars - A static global variable pointing to current sysvar values, | ||
| accessible within the VM by a pointer to its murmur hash code resolved during | ||
| JIT compilation. | ||
|
|
||
| ## Detailed Design | ||
|
|
||
| As with static syscalls, we define the hash code for a sysvar as the murmur32 | ||
| hash of its respective name: | ||
|
|
||
| ```rust | ||
| // murmur3(SOL_RENT_SYSVAR) = 0x494df715 | ||
| const SOL_RENT_SYSVAR: *const u8 = 0x494df715u64 as *const u8; | ||
| // murmur3(SOL_CLOCK_SYSVAR) = 0xff395088 | ||
| const SOL_CLOCK_SYSVAR: *const u8 = 0xff395088u64 as *const u8; | ||
| ``` | ||
|
|
||
| We cast this value to a pointer, which is then consumed in a program: | ||
|
|
||
| ```rust | ||
| let lamports_per_byte: u64 = unsafe { *(SOL_RENT_SYSVAR as *const u64) }; | ||
| ``` | ||
|
|
||
| This produces the following bytecode: | ||
|
|
||
| ```asm | ||
| lddw r3, 0x494df715 // SOL_RENT_SYSVAR | ||
| ldxdw r3, [r3+0] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would benefit from explaining that sysvars are mapped in as byte slices starting at the murmur hash as address. Otherwise it invites the interpretation that all sysvars are supposed to be accessed via a single |
||
| ``` | ||
|
|
||
| In this case, the murmur hash value of `0x494df715` is then resolved JIT to a | ||
| memory address containing the current `Rent` sysvar value. The same applies to | ||
| other sysvar accounts, such as the murmur hash of `0xff395088` for `Clock`. | ||
|
|
||
| Ergo, we can safely and performantly expose any available global variable to | ||
| the VM without the overhead of additonal account loads or syscall invocation. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, this thinks about the JIT only, which is not protocol, it is one possible implementation. What about an interpreter? It would have to do this in address translation on every memory access. Also, while maybe not that impactful, it would also make the JIT implementation more complex and thus compilation slower as this is a form of macro-op fusion which requires state to be tracked between the instructions. Currently we can compile every instruction independently. |
||
|
|
||
| ## Alternatives Considered | ||
|
|
||
| - Leverage JIT intrinsics to provide similar syscall functionality. | ||
| - Don't improve the existing design of sysvars/syscalls. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think taking another look at solving this problem using sysvar accounts is worthwhile. The gap to bridge there might be relatively small (see https://github.com/solana-foundation/solana-improvement-documents/pull/503/changes#r3002020193). |
||
|
|
||
| ## Impact | ||
|
|
||
| 1. Improved program composability | ||
| 2. Fewer stack allocations | ||
| 3. Reduced complexity of calculating rent exemption | ||
| 4. Reduced cost of resolving sysvar values to 2 CUs | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| 1. We must ensure no intra-slot mutability of any exposed globals. | ||
| 2. We must ensure static sysvar pointers remain synchronized with SysvarCache | ||
| at the slot boundary. | ||
| 3. Despite being 32-bit hashes, it is important that we cast to u64 first as | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively one could also only use the 31 LSBs of the hash / mask out the 1 MSB and always load a |
||
| 50% of 32-bit murmur hashes sign extend to negative 64-bit addresses. If | ||
| our toolchain reliably generated `mov32` without requiring inline assembly, | ||
| this would be more ideal, as it would save 8 bytes of binary size. | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| This feature is a breaking change that will require feature-gated activation. | ||
| Realizing the performance benefits of static sysvars will require adoption by | ||
| all relevant programs and SDKs. All existing Syscall/Sysvar APIs will continue | ||
| to function as normal. | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit on terminology: I know we called them "static syscalls" but they are not static linking (that would imply we would inline their implementation into the SBPF ELF at or before deployment), instead they are still dynamic linking but relocation-less.
The infrastructure for "static syscalls" is for syscalls only, as the name implies. Linking of (in this case readonly) data is a different process and there is no existing infrastructure to utilize.
I know we have used JIT-only mode on MNB in Agave for years but with the next version we could have tiered compilation (Interpreter & JIT hybrid). Also Firedancer is using an Interpreter.