Skip to content

Commit fca89da

Browse files
committed
Create proposal
1 parent f9e6c7b commit fca89da

File tree

1 file changed

+259
-0
lines changed

1 file changed

+259
-0
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
---
2+
simd: '0377'
3+
title: eBPF ISA compatibility
4+
authors:
5+
- Lucas Steuernagel (Anza)
6+
- Alexander Meißner (Anza)
7+
category: Standard
8+
type: Core
9+
status: Review
10+
created: 2025-10-09
11+
feature: (fill in with feature key and github tracking issues once accepted)
12+
---
13+
14+
## Summary
15+
16+
This SIMD introduces instruction set architecture (ISA) changes to make the
17+
sBPF virtual machine compatible with the latest existing version of eBPF ISA
18+
generated by its LLVM backend.
19+
20+
It reverts past ISA changes, modifies the encoding of existing instructions
21+
and brings new instructions to the Solana virtual machine.
22+
23+
## Motivation
24+
25+
The eBPF target on the Rust compiler emits code by default for eBPFv1, whose
26+
only incompatibility with the Solana virtual machine is the `callx`
27+
instruction. Aiming to prioritize Solana programs and decrease their CU
28+
consumption, we want to be compatible with at least the current eBPF version
29+
(v3), which brings in new instructions. In order for that to be possible, we
30+
must modify our virtual machine to support eBPF integrally.
31+
32+
Additionally, we are going to anticipate some eBPF v4 instructions that are
33+
beneficial for Solana and would decrease the update burden in case v4 becomes
34+
the new default configuration for the eBPF upstream LLVM target.
35+
36+
## New Terminology
37+
38+
The set containing these new instructions will form an sBPFv3 program.
39+
40+
## Detailed Design
41+
42+
### ELF Identification
43+
44+
Programs containing the instructions mentioned in this SIMD must have the
45+
`0x03` value in the `e_flags` field of their header.
46+
47+
### Revert SIMD-0166
48+
49+
SIMD-0166 must be reverted beginning with sBPFv3, since we will introduce a
50+
new design for dynamic stack frames that is closer to the eBPF code generation.
51+
52+
### Revert SIMD-0173
53+
54+
All changes proposed in SIMD-173 will no longer take effect in sBPFv3.
55+
Consequently, the verifier must accept the following opcodes:
56+
57+
- `0x18`, `0x00` (`LDDW`)
58+
- `0x72`, `0x71`, `0x73` (`STB`, `LDXB`, `STXB`)
59+
- `0x6A`, `0x69`, `0x6B` (`STH`, `LDXH`, `STXH`)
60+
- `0x62`, `0x61`, `0x63` (`STW`, `LDXW`, `STXW`)
61+
- `0x7A`, `0x79`, `0x7B` (`STDW`, `LDXDW`, `STXDW`)
62+
- `0xD4` (`LE`)
63+
64+
The new opcodes introduced in SIMD-173 must be rejected in the verifier with `VerifierError::UnknownOpCode`:
65+
66+
- the `HOR64` instruction (opcode `0xF7`)
67+
- the moved opcodes:
68+
- `0x27`, `0x2C`, `0x2F` (`STB`, `LDXB`, `STXB`)
69+
- `0x37`, `0x3C`, `0x3F` (`STH`, `LDXH`, `STXH`)
70+
- `0x87`, `0x8C`, `0x8F` (`STW`, `LDXW`, `STXW`)
71+
- `0x97`, `0x9C`, `0x9F` (`STDW`, `LDXDW`, `STXDW`)
72+
73+
### Revert SIMD-0174
74+
75+
All changes proposed in SIMD-174 will no longer take effect in sBPFv3.
76+
Consequently, the verifier must accept the following opcodes:
77+
78+
- the `MUL` instruction (opcodes `0x24`, `0x2C`, `0x27` and `0x2F`)
79+
- the `DIV` instruction (opcodes `0x34`, `0x3C`, `0x37` and `0x3F`)
80+
- the `MOD` instruction (opcodes `0x94`, `0x9C`, `0x97` and `0x9F`)
81+
- the `NEG` instruction (opcodes `0x84` and `0x87`)
82+
83+
The verifier must reject programs and throw `VerifierError::UnknownOpCode` for
84+
programs that contain any of the following opcodes.
85+
86+
- the `UHMUL64` instruction (opcode `0x36` and `0x3E`)
87+
- the `UDIV32` instruction (opcode `0x46` and `0x4E`)
88+
- the `UDIV64` instruction (opcode `0x56` and `0x5E`)
89+
- the `UREM32` instruction (opcode `0x66` and `0x6E`)
90+
- the `UREM64` instruction (opcode `0x76` and `0x7E`)
91+
- the `LMUL32` instruction (opcode `0x86` and `0x8E`)
92+
- the `LMUL64` instruction (opcode `0x96` and `0x9E`)
93+
- the `SHMUL64` instruction (opcode `0xB6` and `0xBE`)
94+
- the `SDIV32` instruction (opcode `0xC6` and `0xCE`)
95+
- the `SDIV64` instruction (opcode `0xD6` and `0xDE`)
96+
- the `SREM32` instruction (opcode `0xE6` and `0xEE`)
97+
- the `SREM64` instruction (opcode `0xF6` and `0xFE`)
98+
99+
### Execution changes
100+
101+
MOV32_REG (opcode `0x14`) must NOT perform sign extension.
102+
103+
SUB32_IMM and SUB64_IMM must perform the operation `src = src - imm`.
104+
105+
### Dynamic stack frames
106+
107+
Aiming a closer compatibility to eBPF, the implementation of dynamic stack
108+
frames is going to change.
109+
110+
The R10 register must continue to be the frame pointer, i.e. pointing to the
111+
highest address accessible in a function. As such, the stack will grow upwards.
112+
113+
At the prologue of each function, there may be one ADD64 (opcode 0x07)
114+
instruction to adjust the frame pointer to its new position with a positive
115+
offset (`add64 R10, +imm`). When a function returns, the virtual machine will
116+
automatically restore the frame pointer register with the value used in the
117+
caller, so programs do not need to emit any instruction to adjust the frame
118+
pointer in the epilogue of each function.
119+
120+
### JMP32 instruction class
121+
122+
The JMP32 instruction class utilizes 32 bit wide operands for the same
123+
operations as the JMP class.
124+
125+
The following opcodes must be allowed in the verifier and the virtual machine
126+
must implement the behavior described below for each one of them.
127+
128+
- `JEQ32_IMM` -> opcode = `0x16` -> `pc += offset if dst as u32 == IMM as u32`
129+
- `JGT32_IMM` -> opcode = `0x26` -> `pc += offset if dst as u32 > IMM as u32`
130+
- `JGE32_IMM` -> opcode = `0x36` -> `pc += offset if dst as u32 >= IMM as u32`
131+
- `JSET32_IMM` -> opcode = `0x46` ->
132+
`pc += offset if (dst as u32 & IMM as u32) != 0`
133+
- `JNE32_IMM` -> opcode = `0x56` -> `pc += offset if dst as u32 != IMM as u32`
134+
- `JSGT32_IMM` -> opcode = `0x66` -> `pc += offset if dst as i32 > IMM as i32`
135+
- `JSGE32_IMM` -> opcode = `0x76` -> `pc += offset if dst as i32 > IMM as i32`
136+
- `JLT32_IMM` -> opcode = `0xa6` -> `pc += offset if dst as u32 < IMM as u32`
137+
- `JLE32_IMM` -> opcode = `0xb6` -> `pc += offset if dst as u32 <= IMM as u32`
138+
- `JSLT32_IMM` -> opcode = `0xc6` -> `pc += offset if dst as i32 < IMM as i32`
139+
- `JSLE32_IMM` -> opcode = `0xd6` -> `pc += offset if dst as i32 <= IMM as i32`
140+
141+
- `JEQ32_REG` -> opcode = `0x1e` -> `pc += offset if dst as u32 == src as u32`
142+
- `JGT32_REG` -> opcode = `0x2e` -> `pc += offset if dst as u32 > src as u32`
143+
- `JGE32_REG` -> opcode = `0x3e` -> `pc += offset if dst as u32 >= src as u32`
144+
- `JSET32_REG` -> opcode = `0x4e` ->
145+
`pc += offset if (dst as u32 & src as u32) != 0`
146+
- `JNE32_REG` -> opcode = `0x56` -> `pc += offset if dst as u32 != src as u32`
147+
- `JSGT32_REG` -> opcode = `0x66` -> `pc += offset if dst as i32 > src as i32`
148+
- `JSGE32_REG` -> opcode = `0x76` -> `pc += offset if dst as i32 > src as i32`
149+
- `JLT32_REG` -> opcode = `0xa6` -> `pc += offset if dst as u32 < src as u32`
150+
- `JLE32_REG` -> opcode = `0xb6` -> `pc += offset if dst as u32 <= src as u32`
151+
- `JSLT32_REG` -> opcode = `0xc6` -> `pc += offset if dst as i32 < src as i32`
152+
- `JSLE32_REG` -> opcode = `0xd6` -> `pc += offset if dst as i32 <= src as i32`
153+
154+
### SMOD and SDIV instructions
155+
156+
The following opcodes must be allowed in the verifier for a sBPFv3 program and
157+
the following behavior must occur in the virtual machine.
158+
159+
- `SMOD64_IMM` -> opcode = `0x97` -> `dst = dst as i64 % imm as i64`
160+
- `SMOD64_REG` -> opcode = `0x9f` -> `dst = dst as i64 % src as i64`
161+
- `SMOD32_IMM` -> opcode = `0x94` -> `dst = dst as i32 % imm as i32`
162+
- `SMOD32_REG` -> opcode = `0x9c` -> `dst = dst as i32 % src as i32`
163+
- `SDIV64_IMM` -> opcode = `0x37` -> `dst = dst as i64 / imm as i64`
164+
- `SDIV64_REG` -> opcode = `0x3f` -> `dst = dst as i64 / src as i64`
165+
- `SDIV32_IMM` -> opcode = `0x34` -> `dst = dst as i32 / imm as i32`
166+
- `SDIV32_REG` -> opcode = `0x3c` -> `dst = dst as i32 / src as i32`
167+
168+
### Sign extended mov and sign extended load
169+
170+
The verifier must accept the following instruction encodings for sign extended
171+
`mov` operations, and the virtual machine must implement the behavior detailed
172+
below for them.
173+
174+
The existing `MOV64` and `MOV32` instructions were included in the list below
175+
only for comparison.
176+
177+
- `MOV64` -> opcode = `0xbf`, offset = `0` -> `dst = src as i64`
178+
(existing instruction - only here for comparison)
179+
- `MOV64S8` -> opcode = `0xbf`, offset = `8` -> `dst = src as i8 as i64`
180+
- `MOV64S16` -> opcode = `0xbf`, offset = `16` -> `dst = src as i16 as i64`
181+
- `MOV64S32` -> opcode = `0xbf`, offset = `32` -> `dst = src as i32 as i64`
182+
183+
- `MOV32` -> opcode = `0xbc`, offset = `0` -> `dst = src as u32`
184+
(existing instruction - only here for comparison)
185+
- `MOV32S8` -> opcode = `0xbc`, offset = `8` -> `dst = src as i8 as i32`
186+
- `MOV32S16` -> opcode = `0xbc`, offset = `16` -> `dst = src as i16 as i32`
187+
188+
189+
### Indirect jump
190+
191+
The indirect jump instruction `jx` jumps to the instruction pointed by the
192+
address in the source register. In sum, the verifier must allow the following
193+
opcode and the runtime must implement the following behavior.
194+
195+
- `jx` -> opcode = `0x0d` -> pc = `src`
196+
197+
### callx encoding
198+
199+
The encoding of callx must change so that the register containing the address
200+
to jump to is in the destination register.
201+
202+
- `callx` -> opcode = `0x9d` -> pc = `dst`
203+
204+
205+
### Reinterpretation of LDDW as MOV and HOR
206+
207+
The LDDW instruction consists of two 8-byte instruction frames, but consumes
208+
only one CU in the virtual machine.
209+
210+
The virtual machine must now interpret the first half of LLDW as the opcode
211+
`0x18`, with the same behavior as `mov32 reg, imm` zero extending the
212+
immediate value.
213+
214+
Likewise, the second half of LDDW must be interpreted as the opcode `0x00`,
215+
being a bitwise OR operation of the MSBs in the destination register. This
216+
instruction must be called `hor64 reg, imm`.
217+
218+
The HOR instruction, however, must encode a destination register on which to
219+
operate. The encoding is as follows.
220+
221+
- `HOR64` -> opcode = `0x00` -> `dst = dst | (imm << 32)`
222+
223+
Consequently, we must charge two CUs for an LDDW execution.
224+
225+
## Alternatives Considered
226+
227+
We have considered diverging from the eBPF standard by introducing new opcodes
228+
and creating specific instructions to the Solana environment. We discarded
229+
such an approach to be compatible with the existing LLVM eBPF code generation.
230+
231+
We are not adding `may_goto`, since it has an implicit condition implemented
232+
in the kernel, and does not yet have any path for code generation. We are also
233+
not supporting any of the eBPF atomic instructions, since the Solana virtual
234+
machine is single threaded.
235+
236+
In eBPFv4, there are two other instructions that could be part of the Solana
237+
vendored virtual machine, but are not included in the proposal: `gotol`
238+
(opcode `0x06`) and `bswap` (opcode `0xd7`).
239+
240+
`gotol` is an unconditional jump with a 32 bit offset. It does not replace the
241+
existing JA instruction (opcode 0x05), and is used only when the 16-bit offset
242+
from the JA can't be used for a jump. This situation appears only in very
243+
large functions, or in environments with aggressive inlining, and has not so
244+
far represented a problem for smart contracts.
245+
246+
`bswap` supersedes LE (opcode 0xd4) and BE (opcode 0xdc) in eBPFv4, but
247+
otherwise behaves similarly. Byte swap is rarely used as an instruction in
248+
smart contracts, and so far the existing opcodes already fill up any needs.
249+
250+
## Impact
251+
252+
With these changes, and a patch to the aya bpf-linker, developers will be able
253+
to install the bpf-linker and use the existing rustup/cargo/rustc
254+
infrastructure to build their programs.
255+
256+
## Security Considerations
257+
258+
None
259+

0 commit comments

Comments
 (0)