1use crate::dynsym::RelocationType;
2use crate::lexer::{ImmediateValue, Token};
3use crate::syscall::SYSCALLS;
4use crate::errors::CompileError;
5use sbpf_common::opcode::Opcode;
6
7use std::ops::Range;
8
9#[derive(Debug, Clone)]
10pub struct Instruction {
11 pub opcode: Opcode,
12 pub operands: Vec<Token>,
13 pub span: Range<usize>,
14}
15
16impl Instruction {
17 pub fn get_size(&self) -> u64 {
19 match self.opcode {
20 Opcode::Lddw => 16,
21 _ => 8,
22 }
23 }
24 pub fn needs_relocation(&self) -> bool {
26 match self.opcode {
27 Opcode::Call | Opcode::Lddw => {
28 matches!(&self.operands.last(), Some(Token::Identifier(_, _)))
29 }
30 _ => false,
31 }
32 }
33
34 pub fn is_jump(&self) -> bool {
36 matches!(
37 self.opcode,
38 Opcode::Ja
39 | Opcode::JeqImm
40 | Opcode::JgtImm
41 | Opcode::JgeImm
42 | Opcode::JltImm
43 | Opcode::JleImm
44 | Opcode::JsetImm
45 | Opcode::JneImm
46 | Opcode::JsgtImm
47 | Opcode::JsgeImm
48 | Opcode::JsltImm
49 | Opcode::JsleImm
50 | Opcode::JeqReg
51 | Opcode::JgtReg
52 | Opcode::JgeReg
53 | Opcode::JltReg
54 | Opcode::JleReg
55 | Opcode::JsetReg
56 | Opcode::JneReg
57 | Opcode::JsgtReg
58 | Opcode::JsgeReg
59 | Opcode::JsltReg
60 | Opcode::JsleReg
61 )
62 }
63 pub fn get_relocation_info(&self) -> (RelocationType, String) {
65 match self.opcode {
66 Opcode::Lddw => match &self.operands[1] {
67 Token::Identifier(name, _) => (RelocationType::RSbf64Relative, name.clone()),
68 _ => panic!("Expected label operand"),
69 },
70 _ => {
71 if let Token::Identifier(name, _) = &self.operands[0] {
72 (RelocationType::RSbfSyscall, name.clone())
73 } else {
74 panic!("Expected label operand")
75 }
76 }
77 }
78 }
79 pub fn from_bytes(bytes: &[u8]) -> Result<Self, CompileError> {
81 let mut operands = Vec::new();
82 let span = 0..bytes.len();
83
84 let opcode = Opcode::from_u8(bytes[0]).unwrap();
85 let reg = bytes[1];
86 let src = reg >> 4;
87 let dst = reg & 0x0f;
88 let off = i16::from_le_bytes([bytes[2], bytes[3]]);
89 let imm = match opcode {
90 Opcode::Lddw => {
91 let imm_low = i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
92 let imm_high = i32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
93
94 ((imm_high as i64) << 32) | (imm_low as u32 as i64)
95 }
96 _ => i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as i64,
97 };
98
99 match opcode {
100 Opcode::Lddw => {
101 if src != 0 || off != 0 {
102 return Err(CompileError::BytecodeError {
103 error: format!("Lddw instruction expects src and off to be 0, but got src: {}, off: {}", src, off),
104 span: span.clone(),
105 custom_label: None,
106 });
107 }
108 operands.push(Token::Register(dst, 1..2));
109 operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..12));
110 }
111
112 Opcode::Call => {
113 if let Some(name) = SYSCALLS.get(&(imm as u32)) {
114 if reg != 0 || off != 0 {
115 return Err(CompileError::BytecodeError {
116 error: format!("Call instruction with syscall expects reg and off to be 0, but got reg: {}, off: {}", reg, off),
117 span: span.clone(),
118 custom_label: None,
119 });
120 }
121 operands.push(Token::Identifier(name.to_string(), 4..8));
122 } else {
123 if reg != 16 || off != 0 {
124 return Err(CompileError::BytecodeError {
125 error: format!("Call instruction with immediate expects reg to be 16 and off to be 0, but got reg: {}, off: {}", reg, off),
126 span: span.clone(),
127 custom_label: None,
128 });
129 }
130 operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
131 }
132 }
133
134 Opcode::Callx => {
135 if src != 0 || off != 0 || imm != 0 {
136 return Err(CompileError::BytecodeError {
137 error: format!("Callx instruction expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
138 span: span.clone(),
139 custom_label: None,
140 });
141 }
142 operands.push(Token::Register(dst, 1..2));
144 }
145
146 Opcode::Ja => {
147 if reg != 0 || imm != 0 {
148 return Err(CompileError::BytecodeError {
149 error: format!("Ja instruction expects reg and imm to be 0, but got reg: {}, imm: {}", reg, imm),
150 span: span.clone(),
151 custom_label: None,
152 });
153 }
154 operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
155 }
156
157 Opcode::JeqImm
158 | Opcode::JgtImm
159 | Opcode::JgeImm
160 | Opcode::JltImm
161 | Opcode::JleImm
162 | Opcode::JsetImm
163 | Opcode::JneImm
164 | Opcode::JsgtImm
165 | Opcode::JsgeImm
166 | Opcode::JsltImm
167 | Opcode::JsleImm => {
168 if src != 0 {
169 return Err(CompileError::BytecodeError {
170 error: format!("Jump instruction with immediate expects src to be 0, but got src: {}", src),
171 span: span.clone(),
172 custom_label: None,
173 });
174 }
175 operands.push(Token::Register(dst, 1..2));
176 operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
177 operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
178 }
179
180 Opcode::JeqReg
181 | Opcode::JgtReg
182 | Opcode::JgeReg
183 | Opcode::JltReg
184 | Opcode::JleReg
185 | Opcode::JsetReg
186 | Opcode::JneReg
187 | Opcode::JsgtReg
188 | Opcode::JsgeReg
189 | Opcode::JsltReg
190 | Opcode::JsleReg => {
191 if imm != 0 {
192 return Err(CompileError::BytecodeError {
193 error: format!("Jump instruction with register expects imm to be 0, but got imm: {}", imm),
194 span: span.clone(),
195 custom_label: None,
196 });
197 }
198 operands.push(Token::Register(dst, 1..2));
199 operands.push(Token::Register(src, 1..2));
200 operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
201 }
202
203 Opcode::Add32Imm
205 | Opcode::Sub32Imm
206 | Opcode::Mul32Imm
207 | Opcode::Div32Imm
208 | Opcode::Or32Imm
209 | Opcode::And32Imm
210 | Opcode::Lsh32Imm
211 | Opcode::Rsh32Imm
212 | Opcode::Mod32Imm
213 | Opcode::Xor32Imm
214 | Opcode::Mov32Imm
215 | Opcode::Arsh32Imm
216 | Opcode::Lmul32Imm
217 | Opcode::Udiv32Imm
218 | Opcode::Urem32Imm
219 | Opcode::Sdiv32Imm
220 | Opcode::Srem32Imm
221 | Opcode::Add64Imm
222 | Opcode::Sub64Imm
223 | Opcode::Mul64Imm
224 | Opcode::Div64Imm
225 | Opcode::Or64Imm
226 | Opcode::And64Imm
227 | Opcode::Lsh64Imm
228 | Opcode::Rsh64Imm
229 | Opcode::Mod64Imm
230 | Opcode::Xor64Imm
231 | Opcode::Mov64Imm
232 | Opcode::Arsh64Imm
233 | Opcode::Hor64Imm
234 | Opcode::Lmul64Imm
235 | Opcode::Uhmul64Imm
236 | Opcode::Udiv64Imm
237 | Opcode::Urem64Imm
238 | Opcode::Shmul64Imm
239 | Opcode::Sdiv64Imm
240 | Opcode::Srem64Imm
241 | Opcode::Be
242 | Opcode::Le => {
243 if src != 0 || off != 0 {
244 return Err(CompileError::BytecodeError {
245 error: format!("Arithmetic instruction with immediate expects src and off to be 0, but got src: {}, off: {}", src, off),
246 span: span.clone(),
247 custom_label: None,
248 });
249 }
250 operands.push(Token::Register(dst, 1..2));
251 operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
252 }
253
254 Opcode::Add32Reg
256 | Opcode::Sub32Reg
257 | Opcode::Mul32Reg
258 | Opcode::Div32Reg
259 | Opcode::Or32Reg
260 | Opcode::And32Reg
261 | Opcode::Lsh32Reg
262 | Opcode::Rsh32Reg
263 | Opcode::Mod32Reg
264 | Opcode::Xor32Reg
265 | Opcode::Mov32Reg
266 | Opcode::Arsh32Reg
267 | Opcode::Lmul32Reg
268 | Opcode::Udiv32Reg
269 | Opcode::Urem32Reg
270 | Opcode::Sdiv32Reg
271 | Opcode::Srem32Reg
272 | Opcode::Add64Reg
273 | Opcode::Sub64Reg
274 | Opcode::Mul64Reg
275 | Opcode::Div64Reg
276 | Opcode::Or64Reg
277 | Opcode::And64Reg
278 | Opcode::Lsh64Reg
279 | Opcode::Rsh64Reg
280 | Opcode::Mod64Reg
281 | Opcode::Xor64Reg
282 | Opcode::Mov64Reg
283 | Opcode::Arsh64Reg
284 | Opcode::Lmul64Reg
285 | Opcode::Uhmul64Reg
286 | Opcode::Udiv64Reg
287 | Opcode::Urem64Reg
288 | Opcode::Shmul64Reg
289 | Opcode::Sdiv64Reg
290 | Opcode::Srem64Reg => {
291 if off != 0 || imm != 0 {
292 return Err(CompileError::BytecodeError {
293 error: format!("Arithmetic instruction with register expects off and imm to be 0, but got off: {}, imm: {}", off, imm),
294 span: span.clone(),
295 custom_label: None,
296 });
297 }
298 operands.push(Token::Register(dst, 1..2));
299 operands.push(Token::Register(src, 1..2));
300 }
301
302 Opcode::Ldxw | Opcode::Ldxh | Opcode::Ldxb | Opcode::Ldxdw => {
303 if imm != 0 {
304 return Err(CompileError::BytecodeError {
305 error: format!("Load instruction expects imm to be 0, but got imm: {}", imm),
306 span: span.clone(),
307 custom_label: None,
308 });
309 }
310 operands.push(Token::Register(dst, 1..2));
311 operands.push(Token::Register(src, 1..2));
312 operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
313 }
314
315 Opcode::Stw | Opcode::Sth | Opcode::Stb | Opcode::Stdw => {
316 if src != 0 {
317 return Err(CompileError::BytecodeError {
318 error: format!("Store instruction expects src to be 0, but got src: {}", src),
319 span: span.clone(),
320 custom_label: None,
321 });
322 }
323 operands.push(Token::Register(dst, 1..2));
324 operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
325 operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
326 }
327
328 Opcode::Stxb | Opcode::Stxh | Opcode::Stxw | Opcode::Stxdw => {
329 if imm != 0 {
330 return Err(CompileError::BytecodeError {
331 error: format!("Store instruction with register expects imm to be 0, but got imm: {}", imm),
332 span: span.clone(),
333 custom_label: None,
334 });
335 }
336 operands.push(Token::Register(dst, 1..2));
337 operands.push(Token::Register(src, 1..2));
338 operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
339 }
340
341 Opcode::Neg32 | Opcode::Neg64 | Opcode::Exit => {
343 if src != 0 || off != 0 || imm != 0 {
344 return Err(CompileError::BytecodeError {
345 error: format!("Unary operation expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
346 span: span.clone(),
347 custom_label: None,
348 });
349 }
350 operands.push(Token::Register(dst, 1..2));
351 }
352
353 _ => {
354 return Err(CompileError::BytecodeError {
355 error: format!("Unsupported opcode: {:?}", opcode),
356 span: span.clone(),
357 custom_label: None,
358 });
359 }
360 }
361
362 Ok(Instruction {
363 opcode,
364 operands,
365 span,
366 })
367 }
368}