1extern crate alloc;
2
3use ab_blake3::{CHUNK_LEN, OUT_LEN};
4use ab_contract_file::instruction::{ContractInstruction, ContractRegister};
5use ab_core_primitives::ed25519::{Ed25519PublicKey, Ed25519Signature};
6use ab_io_type::bool::Bool;
7use ab_riscv_interpreter::prelude::*;
8use ab_riscv_primitives::prelude::*;
9use alloc::alloc::{alloc, dealloc, handle_alloc_error};
10use alloc::vec::Vec;
11use core::alloc::Layout;
12use core::fmt;
13use core::hint::cold_path;
14use core::marker::PhantomData;
15use core::mem::offset_of;
16use core::ops::ControlFlow;
17use core::ptr::NonNull;
18
19pub const RISCV_CONTRACT_BYTES: &[u8] = cfg_select! {
21 target_env = "abundance" => &[],
22 _ => {
23 include_bytes!(env!("CONTRACT_PATH"))
24 }
25};
26
27#[derive(Debug, Copy, Clone)]
33#[repr(C)]
34pub struct Blake3HashChunkInternalArgs {
35 chunk_ptr: u64,
36 chunk_size: u32,
37 chunk_capacity: u32,
38 result_ptr: u64,
39 chunk: [u8; CHUNK_LEN],
40 result: [u8; OUT_LEN],
41}
42
43const _: () = {
44 assert!(
45 size_of::<Blake3HashChunkInternalArgs>()
46 == offset_of!(Blake3HashChunkInternalArgs, result) + size_of::<[u8; OUT_LEN]>(),
47 "`Blake3HashChunkInternalArgs` must not have implicit padding"
48 );
49};
50
51impl Blake3HashChunkInternalArgs {
52 pub fn new(internal_args_addr: u64, chunk: [u8; CHUNK_LEN]) -> Self {
54 Self {
55 chunk_ptr: internal_args_addr + offset_of!(Self, chunk) as u64,
56 chunk_size: CHUNK_LEN as u32,
57 chunk_capacity: CHUNK_LEN as u32,
58 result_ptr: internal_args_addr + offset_of!(Self, result) as u64,
59 chunk,
60 result: [0; _],
61 }
62 }
63
64 pub fn result(&self) -> [u8; OUT_LEN] {
66 self.result
67 }
68}
69
70#[derive(Debug, Copy, Clone)]
76#[repr(C)]
77pub struct Ed25519VerifyInternalArgs {
78 pub public_key_ptr: u64,
79 pub public_key_size: u32,
80 pub public_key_capacity: u32,
81 pub signature_ptr: u64,
82 pub signature_size: u32,
83 pub signature_capacity: u32,
84 pub message_ptr: u64,
85 pub message_size: u32,
86 pub message_capacity: u32,
87 pub result_ptr: u64,
88 pub public_key: Ed25519PublicKey,
89 pub signature: Ed25519Signature,
90 pub message: [u8; OUT_LEN],
91 pub result: Bool,
92 pub padding: [u8; 7],
97}
98
99const _: () = {
100 assert!(
101 size_of::<Ed25519VerifyInternalArgs>()
102 == offset_of!(Ed25519VerifyInternalArgs, padding) + size_of::<[u8; 7]>(),
103 "`Ed25519VerifyInternalArgs` must not have implicit padding"
104 );
105};
106
107impl Ed25519VerifyInternalArgs {
108 pub fn new(
110 internal_args_addr: u64,
111 public_key: Ed25519PublicKey,
112 signature: Ed25519Signature,
113 message: [u8; OUT_LEN],
114 ) -> Self {
115 Self {
116 public_key_ptr: internal_args_addr + offset_of!(Self, public_key) as u64,
117 public_key_size: Ed25519PublicKey::SIZE as u32,
118 public_key_capacity: Ed25519PublicKey::SIZE as u32,
119 signature_ptr: internal_args_addr + offset_of!(Self, signature) as u64,
120 signature_size: Ed25519Signature::SIZE as u32,
121 signature_capacity: Ed25519Signature::SIZE as u32,
122 message_ptr: internal_args_addr + offset_of!(Self, message) as u64,
123 message_size: OUT_LEN as u32,
124 message_capacity: OUT_LEN as u32,
125 result_ptr: internal_args_addr + offset_of!(Self, result) as u64,
126 public_key,
127 signature,
128 message,
129 result: Bool::new(false),
130 padding: [0; _],
131 }
132 }
133
134 pub fn result(&self) -> Bool {
136 self.result
137 }
138}
139
140#[derive(Debug, Copy, Clone)]
142#[repr(align(16))]
143pub struct TestMemory<const BASE_ADDR: u64, const SIZE: usize> {
144 data: [u8; SIZE],
145}
146
147impl<const BASE_ADDR: u64, const SIZE: usize> VirtualMemory for TestMemory<BASE_ADDR, SIZE> {
148 #[inline(always)]
149 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
150 where
151 T: BasicInt,
152 {
153 let offset = address.wrapping_sub(BASE_ADDR);
154
155 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
156 cold_path();
157 return Err(VirtualMemoryError::OutOfBoundsRead { address });
158 }
159
160 unsafe {
162 Ok(self
163 .data
164 .as_ptr()
165 .cast::<T>()
166 .byte_add(offset as usize)
167 .read_unaligned())
168 }
169 }
170
171 #[inline(always)]
172 unsafe fn read_unchecked<T>(&self, address: u64) -> T
173 where
174 T: BasicInt,
175 {
176 unsafe {
178 let offset = address.unchecked_sub(BASE_ADDR) as usize;
179 self.data
180 .as_ptr()
181 .cast::<T>()
182 .byte_add(offset)
183 .read_unaligned()
184 }
185 }
186
187 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
188 let offset = address.wrapping_sub(BASE_ADDR);
189
190 if offset > self.data.len() as u64 {
191 cold_path();
192 return Err(VirtualMemoryError::OutOfBoundsRead { address });
193 }
194
195 self.data
196 .get(offset as usize..)
197 .and_then(|data| data.get(..len as usize))
198 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })
199 }
200
201 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8] {
202 let offset = address.wrapping_sub(BASE_ADDR);
203
204 if offset > self.data.len() as u64 {
205 cold_path();
206 return &[];
207 }
208
209 let remaining = self.data.get(offset as usize..).unwrap_or_default();
210 remaining.get(..len as usize).unwrap_or(remaining)
211 }
212
213 #[inline(always)]
214 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
215 where
216 T: BasicInt,
217 {
218 let offset = address.wrapping_sub(BASE_ADDR);
219
220 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
221 cold_path();
222 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
223 }
224
225 unsafe {
227 self.data
228 .as_mut_ptr()
229 .cast::<T>()
230 .byte_add(offset as usize)
231 .write_unaligned(value);
232 }
233
234 Ok(())
235 }
236
237 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError> {
238 let offset = address.wrapping_sub(BASE_ADDR);
239
240 if offset > self.data.len() as u64 {
241 cold_path();
242 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
243 }
244
245 let len = data.len();
246 let Some(target_data) = self
247 .data
248 .get_mut(offset as usize..)
249 .and_then(|data| data.get_mut(..len))
250 else {
251 cold_path();
252 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
253 };
254
255 target_data.copy_from_slice(data);
256
257 Ok(())
258 }
259}
260
261impl<const BASE_ADDR: u64, const SIZE: usize> Default for TestMemory<BASE_ADDR, SIZE> {
262 fn default() -> Self {
263 Self { data: [0; SIZE] }
264 }
265}
266
267impl<const BASE_ADDR: u64, const SIZE: usize> TestMemory<BASE_ADDR, SIZE> {
268 pub fn get_mut_bytes(
270 &mut self,
271 address: u64,
272 size: usize,
273 ) -> Result<&mut [u8], VirtualMemoryError> {
274 let Some(offset) = address.checked_sub(BASE_ADDR) else {
275 cold_path();
276 return Err(VirtualMemoryError::OutOfBoundsRead { address });
277 };
278 let offset = offset as usize;
279
280 let Some(slice) = self
281 .data
282 .get_mut(offset..)
283 .and_then(|data| data.get_mut(..size))
284 else {
285 cold_path();
286 return Err(VirtualMemoryError::OutOfBoundsRead { address });
287 };
288
289 Ok(slice)
290 }
291}
292
293#[derive(Debug, Copy, Clone)]
295pub struct LazyInstructionFetcher {
296 return_trap_address: u64,
297 pc: u64,
298}
299
300impl<Memory> ProgramCounter<u64, Memory> for LazyInstructionFetcher
301where
302 Memory: VirtualMemory,
303{
304 #[inline(always)]
305 fn get_pc(&self) -> u64 {
306 self.pc
307 }
308
309 #[inline(always)]
310 unsafe fn try_set_pc_relative(&mut self, instruction_size: u8, offset: i32) -> bool {
311 let old_pc = <Self as ProgramCounter<_, Memory>>::old_pc(self, instruction_size);
312 let pc = old_pc.wrapping_add_signed(i64::from(offset));
313 self.pc = pc;
316
317 pc != self.return_trap_address
318 && pc.is_multiple_of(u64::from(
319 ContractInstruction::<ContractRegister>::alignment(),
320 ))
321 }
322
323 #[cold]
324 #[inline(never)]
325 unsafe fn failed_branch(
326 &mut self,
327 memory: &Memory,
328 ) -> Result<ControlFlow<()>, ExecutionError<u64>> {
329 self.set_pc(memory, self.pc)
332 }
333
334 #[inline]
335 fn set_pc(&mut self, memory: &Memory, pc: u64) -> Result<ControlFlow<()>, ExecutionError<u64>> {
336 if pc == self.return_trap_address {
337 cold_path();
338 return Ok(ControlFlow::Break(()));
339 }
340
341 if !pc.is_multiple_of(u64::from(
342 ContractInstruction::<ContractRegister>::alignment(),
343 )) {
344 cold_path();
345 return Err(ExecutionError::UnalignedInstruction {
346 address: PackedAddress::new(pc),
347 });
348 }
349
350 if let Err(error) = memory.read::<u32>(pc) {
354 cold_path();
355 return Err(error.into());
356 }
357
358 self.pc = pc;
359
360 Ok(ControlFlow::Continue(()))
361 }
362}
363
364impl<Memory> InstructionFetcher<ContractInstruction, Memory> for LazyInstructionFetcher
365where
366 Memory: VirtualMemory,
367{
368 #[inline]
369 fn peek_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<ContractInstruction> {
370 let instruction = unsafe { memory.read_unchecked(self.pc) };
374 let instruction =
376 unsafe { ContractInstruction::try_decode(instruction).unwrap_unchecked() };
377
378 FetchInstructionResult::Instruction(instruction)
379 }
380
381 #[inline]
382 unsafe fn advance(&mut self, instruction_size: u8) {
383 self.pc = self.pc.wrapping_add(u64::from(instruction_size));
384 }
385
386 #[inline]
387 fn fetch_instruction(
388 &mut self,
389 memory: &Memory,
390 ) -> FetchInstructionResult<ContractInstruction> {
391 let result =
392 InstructionFetcher::<ContractInstruction, Memory>::peek_instruction(self, memory);
393
394 if let FetchInstructionResult::Instruction(instruction) = result {
395 unsafe {
398 InstructionFetcher::<ContractInstruction, Memory>::advance(
399 self,
400 instruction.size(),
401 );
402 }
403 }
404
405 result
406 }
407}
408
409impl LazyInstructionFetcher {
410 #[inline(always)]
419 pub unsafe fn new(return_trap_address: u64, pc: u64) -> Self {
420 Self {
421 return_trap_address,
422 pc,
423 }
424 }
425}
426
427#[derive(Debug)]
435#[repr(C)]
436struct EagerTestInstructionFetcherState {
437 instructions_len: usize,
439 base_addr: u64,
441 return_trap_address: u64,
443}
444
445pub struct EagerTestInstructions {
453 state: NonNull<EagerTestInstructionFetcherState>,
460}
461
462impl fmt::Debug for EagerTestInstructions {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 f.debug_struct("EagerTestInstructions")
465 .field("instructions_len", &self.instructions_len())
466 .field("base_addr", &self.base_addr())
467 .field("return_trap_address", &self.return_trap_address())
468 .finish_non_exhaustive()
469 }
470}
471
472impl Drop for EagerTestInstructions {
473 fn drop(&mut self) {
474 let layout = Self::allocation_layout(self.instructions_len());
475
476 unsafe {
479 dealloc(self.state.as_ptr().cast::<u8>(), layout);
480 }
481 }
482}
483
484impl EagerTestInstructions {
485 const INSTRUCTIONS_OFFSET: usize = size_of::<EagerTestInstructionFetcherState>()
488 .next_multiple_of(align_of::<ContractInstruction>());
489
490 fn allocation_layout(instructions_len: usize) -> Layout {
493 let (layout, instructions_offset) = Layout::new::<EagerTestInstructionFetcherState>()
494 .extend(
495 Layout::array::<ContractInstruction>(instructions_len)
496 .expect("Decoded instructions fit into memory, they were just allocated; qed"),
497 )
498 .expect("Decoded instructions fit into memory, they were just allocated; qed");
499
500 debug_assert_eq!(instructions_offset, Self::INSTRUCTIONS_OFFSET);
501
502 layout.pad_to_align()
503 }
504
505 fn instantiate(
506 instructions: &[ContractInstruction],
507 base_addr: u64,
508 return_trap_address: u64,
509 ) -> Self {
510 let instructions_len = instructions.len();
511 let layout = Self::allocation_layout(instructions_len);
512 #[expect(
513 clippy::cast_ptr_alignment,
514 reason = "Layout is configured to produce correctly aligned memory"
515 )]
516 let state = unsafe { alloc(layout) }.cast::<EagerTestInstructionFetcherState>();
518 let Some(state) = NonNull::new(state) else {
519 handle_alloc_error(layout);
520 };
521
522 unsafe {
524 state.write(EagerTestInstructionFetcherState {
525 instructions_len,
526 base_addr,
527 return_trap_address,
528 });
529 }
530
531 let instance = Self { state };
532
533 unsafe {
536 instance.instructions().copy_from_nonoverlapping(
537 NonNull::from(instructions).cast::<ContractInstruction>(),
538 instructions_len,
539 );
540 }
541
542 instance
543 }
544
545 #[inline(always)]
547 fn instructions(&self) -> NonNull<ContractInstruction> {
548 unsafe { self.state.byte_add(Self::INSTRUCTIONS_OFFSET) }.cast::<ContractInstruction>()
551 }
552
553 #[inline(always)]
555 fn instructions_len(&self) -> usize {
556 unsafe { (*self.state.as_ptr()).instructions_len }
558 }
559
560 #[inline(always)]
562 fn base_addr(&self) -> u64 {
563 unsafe { (*self.state.as_ptr()).base_addr }
565 }
566
567 #[inline(always)]
569 fn return_trap_address(&self) -> u64 {
570 unsafe { (*self.state.as_ptr()).return_trap_address }
572 }
573
574 #[inline(always)]
579 pub unsafe fn fetcher(&self, pc: u64) -> EagerTestInstructionFetcher<'_> {
580 let instruction_offset = (pc - self.base_addr()) as usize / size_of::<u16>();
581
582 EagerTestInstructionFetcher {
583 next_instruction: unsafe { self.instructions().add(instruction_offset) },
586 state: self.state,
587 instructions: PhantomData,
588 }
589 }
590
591 #[inline(always)]
602 pub unsafe fn decode(instructions: &[u8], return_trap_address: u64, base_addr: u64) -> Self {
603 let mut decoded_instructions: Vec<ContractInstruction> =
604 Vec::with_capacity(instructions.len() / size_of::<u16>());
605
606 let mut offset = 0;
607 while let Some(instruction_bytes) = instructions.get(offset..offset + size_of::<u32>()) {
608 let decoded_instruction = u32::from_le_bytes([
609 instruction_bytes[0],
610 instruction_bytes[1],
611 instruction_bytes[2],
612 instruction_bytes[3],
613 ]);
614 let decoded_instruction = Instruction::try_decode(decoded_instruction).unwrap_or(
617 ContractInstruction::Unimp {
618 rs1: Register::ZERO,
619 rs2: Register::ZERO,
620 },
621 );
622 decoded_instructions.push(decoded_instruction);
623 match decoded_instruction.size() {
624 2 => {
625 offset += 2;
626 }
627 4 => {
628 offset += 2;
633
634 let instruction_word = if let Some(instruction_bytes) =
637 instructions.get(offset..offset + size_of::<u32>())
638 {
639 u32::from_le_bytes([
640 instruction_bytes[0],
641 instruction_bytes[1],
642 instruction_bytes[2],
643 instruction_bytes[3],
644 ])
645 } else {
646 u32::from_le_bytes([instruction_bytes[2], instruction_bytes[3], 0, 0])
647 };
648
649 decoded_instructions.push(Instruction::try_decode(instruction_word).unwrap_or(
650 ContractInstruction::Unimp {
651 rs1: Register::ZERO,
652 rs2: Register::ZERO,
653 },
654 ));
655 offset += 2;
656 }
657 instruction_size => {
658 unreachable!("Invalid instruction size {instruction_size}, expected 2 or 4");
659 }
660 }
661 }
662
663 let remainder_bytes = instructions.get(offset..).unwrap_or(&[]);
664
665 if remainder_bytes.len() == size_of::<u16>() {
666 let instruction_word =
667 u32::from_le_bytes([remainder_bytes[0], remainder_bytes[1], 0, 0]);
668 decoded_instructions.push(Instruction::try_decode(instruction_word).unwrap_or(
669 ContractInstruction::Unimp {
670 rs1: Register::ZERO,
671 rs2: Register::ZERO,
672 },
673 ));
674 }
675
676 Self::instantiate(&decoded_instructions, base_addr, return_trap_address)
677 }
678}
679
680#[derive(Copy, Clone)]
684#[repr(C)]
685pub struct EagerTestInstructionFetcher<'a> {
686 next_instruction: NonNull<ContractInstruction>,
693 state: NonNull<EagerTestInstructionFetcherState>,
695 instructions: PhantomData<&'a EagerTestInstructions>,
697}
698
699const {
700 assert!(size_of::<EagerTestInstructionFetcher<'_>>() == 16);
703 assert!(!core::mem::needs_drop::<EagerTestInstructionFetcher<'_>>());
706}
707
708impl fmt::Debug for EagerTestInstructionFetcher<'_> {
709 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
710 f.debug_struct("EagerTestInstructionFetcher")
711 .field("next_instruction", &self.next_instruction)
712 .field("instructions_len", &self.instructions_len())
713 .field("base_addr", &self.base_addr())
714 .field("return_trap_address", &self.return_trap_address())
715 .finish_non_exhaustive()
716 }
717}
718
719impl<Memory> ProgramCounter<u64, Memory> for EagerTestInstructionFetcher<'_>
720where
721 Memory: VirtualMemory,
722{
723 #[inline(always)]
724 fn get_pc(&self) -> u64 {
725 let decoded_instruction_byte_offset = self
726 .next_instruction
727 .as_ptr()
728 .addr()
729 .wrapping_sub(self.instructions().as_ptr().addr());
730
731 self.base_addr()
732 + decoded_instruction_byte_offset as u64 * size_of::<u16>() as u64
733 / size_of::<ContractInstruction>() as u64
734 }
735
736 #[inline(always)]
745 unsafe fn try_set_pc_relative(&mut self, instruction_size: u8, offset: i32) -> bool {
746 let offset = (offset as isize).wrapping_sub(isize::from(instruction_size));
750 let byte_delta =
753 offset * (size_of::<ContractInstruction>() / size_of::<u16>()).cast_signed();
754 let new_next_instruction = self
758 .next_instruction
759 .as_ptr()
760 .wrapping_byte_offset(byte_delta);
761 self.next_instruction = unsafe { NonNull::new_unchecked(new_next_instruction) };
765
766 let decoded_instruction_byte_offset = new_next_instruction
767 .addr()
768 .wrapping_sub(self.instructions().as_ptr().addr());
769
770 decoded_instruction_byte_offset < self.instructions_len() * size_of::<ContractInstruction>()
776 && decoded_instruction_byte_offset.is_multiple_of(size_of::<ContractInstruction>())
777 }
778
779 #[cold]
782 #[inline(never)]
783 unsafe fn failed_branch(
784 &mut self,
785 memory: &Memory,
786 ) -> Result<ControlFlow<()>, ExecutionError<u64>> {
787 let decoded_instruction_byte_offset = self
790 .next_instruction
791 .as_ptr()
792 .addr()
793 .wrapping_sub(self.instructions().as_ptr().addr())
794 .cast_signed();
795 let address = self.base_addr().wrapping_add_signed(
798 (decoded_instruction_byte_offset
799 / (size_of::<ContractInstruction>() / size_of::<u16>()).cast_signed())
800 as i64,
801 );
802
803 self.set_pc(memory, address)
804 }
805
806 #[inline]
807 fn set_pc(
808 &mut self,
809 _memory: &Memory,
810 pc: u64,
811 ) -> Result<ControlFlow<()>, ExecutionError<u64>> {
812 let address = pc;
813
814 if address == self.return_trap_address() {
815 cold_path();
816 return Ok(ControlFlow::Break(()));
817 }
818
819 if !address.is_multiple_of(size_of::<u16>() as u64) {
820 cold_path();
821 return Err(ExecutionError::UnalignedInstruction {
822 address: PackedAddress::new(address),
823 });
824 }
825
826 let Some(offset) = address.checked_sub(self.base_addr()) else {
827 cold_path();
828 return Err(ExecutionError::OutOfBoundsRead {
829 address: PackedAddress::new(address),
830 });
831 };
832 let offset = offset as usize;
833 let instruction_offset = offset / size_of::<u16>();
834
835 if instruction_offset >= self.instructions_len() {
836 cold_path();
837 return Err(VirtualMemoryError::OutOfBoundsRead { address }.into());
838 }
839
840 self.next_instruction = unsafe { self.instructions().add(instruction_offset) };
842
843 Ok(ControlFlow::Continue(()))
844 }
845}
846
847impl<Memory> InstructionFetcher<ContractInstruction, Memory> for EagerTestInstructionFetcher<'_>
848where
849 Memory: VirtualMemory,
850{
851 #[inline(always)]
852 fn peek_instruction(
853 &mut self,
854 _memory: &Memory,
855 ) -> FetchInstructionResult<ContractInstruction> {
856 let instruction = unsafe { self.next_instruction.read() };
860
861 FetchInstructionResult::Instruction(instruction)
862 }
863
864 #[inline(always)]
865 unsafe fn advance(&mut self, instruction_size: u8) {
866 let byte_advance =
867 usize::from(instruction_size) / size_of::<u16>() * size_of::<ContractInstruction>();
868 self.next_instruction = unsafe {
873 NonNull::new_unchecked(
874 self.next_instruction
875 .as_ptr()
876 .wrapping_byte_add(byte_advance),
877 )
878 };
879 }
880
881 #[inline(always)]
882 fn fetch_instruction(
883 &mut self,
884 memory: &Memory,
885 ) -> FetchInstructionResult<ContractInstruction> {
886 let result =
887 InstructionFetcher::<ContractInstruction, Memory>::peek_instruction(self, memory);
888
889 if let FetchInstructionResult::Instruction(instruction) = result {
890 unsafe {
893 InstructionFetcher::<ContractInstruction, Memory>::advance(
894 self,
895 instruction.size(),
896 );
897 }
898 }
899
900 result
901 }
902}
903
904impl EagerTestInstructionFetcher<'_> {
905 #[inline(always)]
907 fn instructions(&self) -> NonNull<ContractInstruction> {
908 unsafe {
911 self.state
912 .byte_add(EagerTestInstructions::INSTRUCTIONS_OFFSET)
913 }
914 .cast::<ContractInstruction>()
915 }
916
917 #[inline(always)]
919 fn instructions_len(&self) -> usize {
920 unsafe { (*self.state.as_ptr()).instructions_len }
923 }
924
925 #[inline(always)]
927 fn base_addr(&self) -> u64 {
928 unsafe { (*self.state.as_ptr()).base_addr }
931 }
932
933 #[inline(always)]
935 fn return_trap_address(&self) -> u64 {
936 unsafe { (*self.state.as_ptr()).return_trap_address }
939 }
940}