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>
impl<'r, R: RngCore> Generator<'r, R>
Sourcepub(crate) fn new(rng: &'r mut R) -> Self
pub(crate) fn new(rng: &'r mut R) -> Self
Create a fresh program generator from a random number generator state.
Sourcefn select_register(
&mut self,
reg_options: &RegisterSet,
) -> Result<RegisterId, ()>
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.
Sourcefn select_op<'a, T, const SIZE: usize>(
&mut self,
options: &'a [T; SIZE],
) -> &'a T
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.
Sourcefn select_constant_weight_bit_mask(&mut self, num_ones: usize) -> u32
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.
Sourcefn select_nonzero_u32(&mut self, mask: u32) -> u32
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.
Sourcepub(crate) fn generate_program(
&mut self,
output: &mut FixedCapacityVec<Instruction, NUM_INSTRUCTIONS>,
) -> Result<(), Error>
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.
Sourcefn generate_instruction(&mut self) -> Result<(Instruction, RegisterWriter), ()>
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.
Sourcefn choose_opcode(&mut self, pass: Pass) -> Opcode
fn choose_opcode(&mut self, pass: Pass) -> Opcode
Choose an opcode using the current OpcodeSelector
, subject to
stateful constraints on adjacent opcode choices.
Sourcefn instruction_gen_attempt(
&mut self,
pass: Pass,
) -> Result<(Instruction, RegisterWriter), ()>
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.
Sourcefn choose_src_reg(
&mut self,
op: Opcode,
timing_plan: &InstructionPlan,
) -> Result<RegisterId, ()>
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
Sourcefn choose_src_dst_regs(
&mut self,
op: Opcode,
pass: Pass,
writer_info_fn: fn(RegisterId) -> RegisterWriter,
timing_plan: &InstructionPlan,
) -> Result<(RegisterId, RegisterId, RegisterWriter), ()>
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.
Sourcefn choose_src_dst_regs_with_writer_info(
&mut self,
op: Opcode,
pass: Pass,
writer_info: RegisterWriter,
timing_plan: &InstructionPlan,
) -> Result<(RegisterId, RegisterId), ()>
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.
Sourcefn choose_dst_reg(
&mut self,
op: Opcode,
pass: Pass,
writer_info: RegisterWriter,
src: Option<RegisterId>,
timing_plan: &InstructionPlan,
) -> Result<RegisterId, ()>
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.
Sourcefn choose_instruction_with_opcode_plan(
&mut self,
op: Opcode,
pass: Pass,
plan: &InstructionPlan,
) -> Result<(Instruction, RegisterWriter), ()>
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.
Sourcefn commit_instruction_state(
&mut self,
inst: &Instruction,
regw: RegisterWriter,
) -> Result<(), ()>
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.