Struct Generator

Source
pub(crate) struct Generator<'r, R: RngCore> {
    rng: RngBuffer<'r, R>,
    scheduler: Scheduler,
    validator: Validator,
    last_selector_result_op: Option<Opcode>,
}
Expand description

Program generator

Fields§

§rng: RngBuffer<'r, R>

The program generator wraps a random number generator, via RngBuffer.

§scheduler: Scheduler

Keep track of when execution units and registers will be ready, and ultimately generate a list of candidate available registers for any particular cycle.

§validator: Validator

Additional constraints on the entire program and pieces thereof are implemented in this separate Validator module.

§last_selector_result_op: Option<Opcode>

Last Opcode chosen by an instruction selector

Some of the instruction selectors have the notion of avoiding duplicates, but HashX designs this check based on the sequence of selector results rather than the sequence of committed instructions.

Implementations§

Source§

impl<'r, R: RngCore> Generator<'r, R>

Source

pub(crate) fn new(rng: &'r mut R) -> Self

Create a fresh program generator from a random number generator state.

Source

fn select_register( &mut self, reg_options: &RegisterSet, ) -> Result<RegisterId, ()>

Pick a pseudorandom register from a RegisterSet.

Returns Err(()) if the set is empty. Consumes one u32 from the Rng only if the set contains more than one item.

The choice is perfectly uniform only if the register set is a power of two length. Uniformity is not critical here.

Source

fn select_op<'a, T, const SIZE: usize>( &mut self, options: &'a [T; SIZE], ) -> &'a T

Pick a pseudorandom operation from a list of options.

The options slice must be between 2 and 255 options in length. For uniformity and efficiency it’s best if the length is also a power of two. All actual operation lists used by HashX have power-of-two lengths.

Source

fn select_constant_weight_bit_mask(&mut self, num_ones: usize) -> u32

Generate a random u32 bit mask, with a constant number of bits set.

This uses an iterative algorithm that selects one bit at a time using a u8 from the Rng for each, discarding duplicates.

Source

fn select_nonzero_u32(&mut self, mask: u32) -> u32

Generate random nonzero values.

Iteratively picks a random u32, masking it, and discarding results that would be all zero.

Source

pub(crate) fn generate_program( &mut self, output: &mut FixedCapacityVec<Instruction, NUM_INSTRUCTIONS>, ) -> Result<(), Error>

Generate an entire program.

Generates instructions into a provided Vec until the generator state can’t be advanced any further. Runs the whole-program validator. Returns with Error::ProgramConstraints if the program fails these checks. This happens in normal use on a small fraction of seed values.

Source

fn generate_instruction(&mut self) -> Result<(Instruction, RegisterWriter), ()>

Generate the next instruction.

This is a multi-pass approach, starting with a Pass::Original that normally succeeds, followed by a Pass::Retry with simplifications to improve success rate, followed by a timing stall that advances the simulated cycle count forward and tries again with the benefit of newly available registers in the schedule.

This only returns Err(()) if we’ve hit a stopping condition for the program.

Source

fn choose_opcode(&mut self, pass: Pass) -> Opcode

Choose an opcode using the current OpcodeSelector, subject to stateful constraints on adjacent opcode choices.

Source

fn instruction_gen_attempt( &mut self, pass: Pass, ) -> Result<(Instruction, RegisterWriter), ()>

Make one attempt at instruction generation.

This picks an OpcodeSelector, chooses an opcode, then finishes choosing the opcode-specific parts of the instruction. Each of these choices affects the Rng state, and may fail if conditions are not met.

Source

fn choose_src_reg( &mut self, op: Opcode, timing_plan: &InstructionPlan, ) -> Result<RegisterId, ()>

Choose only a source register, depending on the opcode and timing plan

Source

fn choose_src_dst_regs( &mut self, op: Opcode, pass: Pass, writer_info_fn: fn(RegisterId) -> RegisterWriter, timing_plan: &InstructionPlan, ) -> Result<(RegisterId, RegisterId, RegisterWriter), ()>

Choose both a source and destination register using a normal RegisterWriter for two-operand instructions.

Source

fn choose_src_dst_regs_with_writer_info( &mut self, op: Opcode, pass: Pass, writer_info: RegisterWriter, timing_plan: &InstructionPlan, ) -> Result<(RegisterId, RegisterId), ()>

Choose both a source and destination register, with a custom RegisterWriter constraint that doesn’t depend on source register choice.

Source

fn choose_dst_reg( &mut self, op: Opcode, pass: Pass, writer_info: RegisterWriter, src: Option<RegisterId>, timing_plan: &InstructionPlan, ) -> Result<RegisterId, ()>

Choose a destination register only, using source and writer info as well as the current state of the validator.

Source

fn choose_instruction_with_opcode_plan( &mut self, op: Opcode, pass: Pass, plan: &InstructionPlan, ) -> Result<(Instruction, RegisterWriter), ()>

With an Opcode and an execution unit timing plan already in mind, generate the other pieces necessary to fully describe an Instruction.

This can fail if register selection fails.

Source

fn commit_instruction_state( &mut self, inst: &Instruction, regw: RegisterWriter, ) -> Result<(), ()>

Commit all state modifications associated with a chosen instruction that’s certainly being written to the final program.

Returns Ok(()) on success or Err(()) if the new state would no longer be valid for program generation and we’re done writing code.

Auto Trait Implementations§

§

impl<'r, R> Freeze for Generator<'r, R>

§

impl<'r, R> RefUnwindSafe for Generator<'r, R>
where R: RefUnwindSafe,

§

impl<'r, R> Send for Generator<'r, R>
where R: Send,

§

impl<'r, R> Sync for Generator<'r, R>
where R: Sync,

§

impl<'r, R> Unpin for Generator<'r, R>

§

impl<'r, R> !UnwindSafe for Generator<'r, R>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.