Skip to content

Commit b561354

Browse files
committed
Bit Machine: Refactor exec_jet
Split the function into parts: 1. Write input to C read frame 2. Prepare output C write frame 3. Execute jet 4. Read output from C write frame I also find it easier to keep separate input and output buffers. Both buffers are allocated with their final capacity so the performance should be the same. Introduce active_{read, write}_bit_width methods to keep code better readable.
1 parent 8ea7e76 commit b561354

File tree

1 file changed

+94
-53
lines changed

1 file changed

+94
-53
lines changed

src/bit_machine/mod.rs

+94-53
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
99
mod frame;
1010

11+
use std::fmt;
12+
use std::sync::Arc;
13+
use std::{cmp, error};
14+
1115
use crate::analysis;
1216
use crate::dag::{DagLike, NoSharing};
1317
use crate::jet::{Jet, JetFailed};
1418
use crate::node::{self, RedeemNode};
1519
use crate::{Cmr, FailEntropy, Value};
1620
use frame::Frame;
17-
use std::fmt;
18-
use std::sync::Arc;
19-
use std::{cmp, error};
2021

2122
/// An execution context for a Simplicity program
2223
pub struct BitMachine {
@@ -177,6 +178,19 @@ impl BitMachine {
177178
}
178179
}
179180

181+
/// Return the bit width of the active read frame.
182+
fn active_read_bit_width(&self) -> usize {
183+
self.read.last().map(|frame| frame.bit_width()).unwrap_or(0)
184+
}
185+
186+
/// Return the bit width of the active write frame.
187+
fn active_write_bit_width(&self) -> usize {
188+
self.write
189+
.last()
190+
.map(|frame| frame.bit_width())
191+
.unwrap_or(0)
192+
}
193+
180194
/// Add a read frame with some given value in it, as input to the
181195
/// program
182196
pub fn input(&mut self, input: &Value) {
@@ -366,66 +380,93 @@ impl BitMachine {
366380
}
367381

368382
fn exec_jet<J: Jet>(&mut self, jet: J, env: &J::Environment) -> Result<(), JetFailed> {
369-
use simplicity_sys::c_jets::frame_ffi::{c_readBit, c_writeBit, CFrameItem};
370-
use simplicity_sys::c_jets::uword_width;
371-
use simplicity_sys::ffi::UWORD;
383+
use crate::ffi::c_jets::frame_ffi::{c_readBit, c_writeBit, CFrameItem};
384+
use crate::ffi::c_jets::uword_width;
385+
use crate::ffi::ffi::UWORD;
386+
387+
/// Create new C read frame that contains `bit_width` many bits from active read frame.
388+
///
389+
/// Return C read frame together with underlying buffer.
390+
///
391+
/// ## Panics
392+
///
393+
/// Active read frame has fewer bits than `bit_width`.
394+
unsafe fn get_input_frame(
395+
mac: &mut BitMachine,
396+
bit_width: usize,
397+
) -> (CFrameItem, Vec<UWORD>) {
398+
assert!(bit_width <= mac.active_read_bit_width());
399+
let uword_width = uword_width(bit_width);
400+
let mut buffer = vec![0; uword_width];
401+
402+
// Copy bits from active read frame into input frame
403+
let buffer_end = buffer.as_mut_ptr().add(uword_width);
404+
let mut write_frame = CFrameItem::new_write(bit_width, buffer_end);
405+
for _ in 0..bit_width {
406+
let bit = mac.read_bit();
407+
c_writeBit(&mut write_frame, bit);
408+
}
409+
mac.back(bit_width);
372410

373-
// Sanity Check: This should never really fail, but still good to do
374-
if !simplicity_sys::c_jets::sanity_checks() {
375-
return Err(JetFailed);
411+
// Convert input frame into read frame
412+
let buffer_ptr = buffer.as_mut_ptr();
413+
let read_frame = CFrameItem::new_read(bit_width, buffer_ptr);
414+
415+
(read_frame, buffer)
376416
}
377-
let src_ty_bit_width = jet.source_ty().to_bit_width();
378-
let target_ty_bit_width = jet.target_ty().to_bit_width();
379-
380-
let a_frame_size = uword_width(src_ty_bit_width);
381-
let b_frame_size = uword_width(target_ty_bit_width);
382-
// a_frame_size + b_frame_size must be non-zero unless it is a unit to unit jet
383-
if a_frame_size == 0 && b_frame_size == 0 {
384-
return Ok(());
417+
418+
/// Create C write frame that is as wide as `bit_width`.
419+
///
420+
/// Return C write frame together with underlying buffer.
421+
unsafe fn get_output_frame(bit_width: usize) -> (CFrameItem, Vec<UWORD>) {
422+
let uword_width = uword_width(bit_width);
423+
let mut buffer = vec![0; uword_width];
424+
425+
// Return output frame as write frame
426+
let buffer_end = buffer.as_mut_ptr().add(uword_width);
427+
let write_frame = CFrameItem::new_write(bit_width, buffer_end);
428+
429+
(write_frame, buffer)
385430
}
386-
let mut src_buf = vec![0 as UWORD; a_frame_size + b_frame_size];
387-
let src_ptr_end = unsafe { src_buf.as_mut_ptr().add(a_frame_size) }; // A frame write
388-
let src_ptr = src_buf.as_mut_ptr(); // A read frame at ptr start
389-
let dst_ptr_begin = unsafe { src_buf.as_mut_ptr().add(a_frame_size) }; // B read frame at ptr begin
390-
let dst_ptr_end = unsafe { src_buf.as_mut_ptr().add(a_frame_size + b_frame_size) }; // B write frame at ptr end
391-
392-
// For jet from type A -> B
393-
// Jets execution: There is single buffer with a_frame_size + b_frame_size UWORDs
394-
// ------[ A read frame ][ B write frame ]---
395-
// ^ src_ptr ^src_ptr_end(dst_ptr_begin) ^ dst_ptr_end
396-
// 1. Write into C bitmachine using A write frame(= src_ptr_end)
397-
// Precondition satisfied: src_ptr_end is one past the end of slice of UWORDs for A.
398-
let mut a_frame = unsafe { CFrameItem::new_write(src_ty_bit_width, src_ptr_end) };
399-
for _ in 0..src_ty_bit_width {
400-
let bit = self.read_bit();
401-
unsafe {
402-
c_writeBit(&mut a_frame, bit);
431+
432+
/// Write `bit_width` many bits from `buffer` into active write frame.
433+
///
434+
/// ## Panics
435+
///
436+
/// Active write frame has fewer bits than `bit_width`.
437+
fn update_active_write_frame(mac: &mut BitMachine, bit_width: usize, buffer: &[UWORD]) {
438+
assert!(bit_width <= mac.active_write_bit_width());
439+
let buffer_ptr = buffer.as_ptr();
440+
let mut read_frame = unsafe { CFrameItem::new_read(bit_width, buffer_ptr) };
441+
442+
for _ in 0..bit_width {
443+
let bit = unsafe { c_readBit(&mut read_frame) };
444+
mac.write_bit(bit);
403445
}
404446
}
405-
self.back(src_ty_bit_width);
406-
407-
// 2. Execute the jet. src = A read frame, dst = B write frame
408-
// Precondition satisfied: src_ptr is the start of slice of UWORDs of A.
409-
let src_frame = unsafe { CFrameItem::new_read(src_ty_bit_width, src_ptr) };
410-
// Precondition satisfied: dst_ptr_end is one past the end of slice of UWORDs of B.
411-
let mut dst_frame = unsafe { CFrameItem::new_write(target_ty_bit_width, dst_ptr_end) };
412-
let jet_fn = jet.c_jet_ptr();
413-
let c_env = jet.c_jet_env(env);
414-
let res = jet_fn(&mut dst_frame, src_frame, c_env);
415447

416-
if !res {
448+
// Sanity Check: This should never really fail, but still good to do
449+
if !simplicity_sys::c_jets::sanity_checks() {
417450
return Err(JetFailed);
418451
}
419452

420-
// 3. Read the result from B read frame
421-
// Precondition satisfied: dst_ptr_begin is the start of slice of UWORDs of B.
422-
let mut b_frame = unsafe { CFrameItem::new_read(target_ty_bit_width, dst_ptr_begin) };
423-
// Read the value from b_frame
424-
for _ in 0..target_ty_bit_width {
425-
let bit = unsafe { c_readBit(&mut b_frame) };
426-
self.write_bit(bit);
453+
let input_width = jet.source_ty().to_bit_width();
454+
let output_width = jet.target_ty().to_bit_width();
455+
// Input buffer is implicitly referenced by input read frame!
456+
// Same goes for output buffer
457+
let (input_read_frame, _input_buffer) = unsafe { get_input_frame(self, input_width) };
458+
let (mut output_write_frame, output_buffer) = unsafe { get_output_frame(output_width) };
459+
460+
let jet_fn = jet.c_jet_ptr();
461+
let c_env = jet.c_jet_env(env);
462+
let success = jet_fn(&mut output_write_frame, input_read_frame, c_env);
463+
464+
if !success {
465+
Err(JetFailed)
466+
} else {
467+
update_active_write_frame(self, output_width, &output_buffer);
468+
Ok(())
427469
}
428-
Ok(())
429470
}
430471
}
431472

0 commit comments

Comments
 (0)