1
//! Pseudorandom generator for hash programs and parts thereof
2

            
3
use crate::Error;
4
use crate::constraints::{self, Pass, RegisterWriter, Validator};
5
use crate::program::{Instruction, InstructionVec, Opcode};
6
use crate::rand::RngBuffer;
7
use crate::register::{RegisterId, RegisterSet};
8
use crate::scheduler::{InstructionPlan, Scheduler};
9
use rand_core::RngCore;
10

            
11
/// The `model` attempts to document HashX program generation choices,
12
/// separate from the main body of the program generator.
13
mod model {
14
    use crate::constraints::Pass;
15
    use crate::generator::OpcodeSelector;
16
    use crate::program::Opcode;
17
    use crate::scheduler::SubCycle;
18

            
19
    /// Choose the next [`OpcodeSelector`].
20
    ///
21
    /// HashX uses a repeating pattern of opcode selectors, based on the
22
    /// current sub_cycle timestamp simulated for instruction fetch/decode.
23
    /// This gives a roughly constant program layout since most instructions do
24
    /// only take one sub-cycle to decode, but we do skip a sub-cycle every time
25
    /// there's a 2-op instruction (wide mul, target, branch). The repeating
26
    /// selector pattern is designed to ensure these skipped selectors are
27
    /// always the Normal type, so the overall number of multiply and branch
28
    /// instructions stays constant.
29
    ///
30
    /// The basic pattern is `(Mul, Normal, Normal)` but `Branch`, `Target`,
31
    /// and `WideMul` operations are mixed in at fixed locations in a 36-cycle
32
    /// repetition.
33
    ///
34
    /// Normal cycles are all replaced by `ImmediateSrc` if this is the retry
35
    /// pass, so that retries won't need to attempt source register selection
36
    /// in this case.
37
    #[inline(always)]
38
5520889
    pub(super) fn choose_opcode_selector(pass: Pass, sub_cycle: SubCycle) -> OpcodeSelector {
39
5520889
        let n = sub_cycle.as_usize() % 36;
40
5520889
        if n == 1 {
41
161312
            OpcodeSelector::Target
42
5359577
        } else if n == 19 {
43
161312
            OpcodeSelector::Branch
44
5198265
        } else if n == 12 || n == 24 {
45
322624
            OpcodeSelector::WideMul
46
4875641
        } else if n.is_multiple_of(3) {
47
1616741
            OpcodeSelector::Mul
48
        } else {
49
3258900
            match pass {
50
3258261
                Pass::Original => OpcodeSelector::Normal,
51
639
                Pass::Retry => OpcodeSelector::ImmediateSrc,
52
            }
53
        }
54
5520889
    }
55

            
56
    /// Opcode choices for [`OpcodeSelector::WideMul`]
57
    pub(super) const WIDE_MUL_OPS_TABLE: [Opcode; 2] = [Opcode::SMulH, Opcode::UMulH];
58

            
59
    /// Opcode choices for [`OpcodeSelector::ImmediateSrc`]
60
    pub(super) const IMMEDIATE_SRC_OPS_TABLE: [Opcode; 4] = [
61
        Opcode::Rotate,
62
        Opcode::XorConst,
63
        Opcode::AddConst,
64
        Opcode::AddConst,
65
    ];
66

            
67
    /// Opcode choices for [`OpcodeSelector::Normal`]
68
    pub(super) const NORMAL_OPS_TABLE: [Opcode; 8] = [
69
        Opcode::Rotate,
70
        Opcode::XorConst,
71
        Opcode::AddConst,
72
        Opcode::AddConst,
73
        Opcode::Sub,
74
        Opcode::Xor,
75
        Opcode::XorConst,
76
        Opcode::AddShift,
77
    ];
78

            
79
    /// Masks for [`super::Instruction::Branch`] always have a constant
80
    /// number of bits set.
81
    ///
82
    /// The probability of taking a branch is approximately
83
    /// `1.0 / (1 << BRANCH_MASK_BIT_WEIGHT)`
84
    pub(super) const BRANCH_MASK_BIT_WEIGHT: usize = 4;
85
}
86

            
87
/// Program generator
88
pub(crate) struct Generator<'r, R: RngCore> {
89
    /// The program generator wraps a random number generator, via [`RngBuffer`].
90
    rng: RngBuffer<'r, R>,
91

            
92
    /// Keep track of when execution units and registers will be ready,
93
    /// and ultimately generate a list of candidate available registers
94
    /// for any particular cycle.
95
    scheduler: Scheduler,
96

            
97
    /// Additional constraints on the entire program and pieces thereof
98
    /// are implemented in this separate Validator module.
99
    validator: Validator,
100

            
101
    /// Last [`Opcode`] chosen by an instruction selector
102
    ///
103
    /// Some of the instruction selectors have the notion of avoiding
104
    /// duplicates, but `HashX` designs this check based on the sequence of
105
    /// selector results rather than the sequence of committed instructions.
106
    last_selector_result_op: Option<Opcode>,
107
}
108

            
109
impl<'r, R: RngCore> Generator<'r, R> {
110
    /// Create a fresh program generator from a random number generator state.
111
    #[inline(always)]
112
10082
    pub(crate) fn new(rng: &'r mut R) -> Self {
113
10082
        Generator {
114
10082
            rng: RngBuffer::new(rng),
115
10082
            scheduler: Scheduler::new(),
116
10082
            validator: Validator::new(),
117
10082
            last_selector_result_op: None,
118
10082
        }
119
10082
    }
120

            
121
    /// Pick a pseudorandom register from a RegisterSet.
122
    ///
123
    /// Returns `Err(())` if the set is empty. Consumes one `u32` from the `Rng`
124
    /// only if the set contains more than one item.
125
    ///
126
    /// The choice is perfectly uniform only if the register set is a power of
127
    /// two length. Uniformity is not critical here.
128
    #[inline(always)]
129
7877734
    fn select_register(&mut self, reg_options: &RegisterSet) -> Result<RegisterId, ()> {
130
7877734
        match reg_options.len() {
131
5538
            0 => Err(()),
132
82076
            1 => Ok(reg_options.index(0)),
133
7790120
            num_options => {
134
7790120
                let num_options: u32 = num_options
135
7790120
                    .try_into()
136
7790120
                    .expect("register set length always fits in u32");
137
7790120
                let index = self.rng.next_u32() % num_options;
138
7790120
                Ok(reg_options.index(
139
7790120
                    index
140
7790120
                        .try_into()
141
7790120
                        .expect("register set length always fits in usize"),
142
7790120
                ))
143
            }
144
        }
145
7877734
    }
146

            
147
    /// Pick a pseudorandom operation from a list of options.
148
    ///
149
    /// The `options` slice must be between 2 and 255 options in length.
150
    /// For uniformity and efficiency it's best if the length is also a power
151
    /// of two. All actual operation lists used by HashX have power-of-two
152
    /// lengths.
153
    #[inline(always)]
154
3581524
    fn select_op<'a, T, const SIZE: usize>(&mut self, options: &'a [T; SIZE]) -> &'a T {
155
3581524
        &options[(self.rng.next_u8() as usize) % options.len()]
156
3581524
    }
157

            
158
    /// Generate a random u32 bit mask, with a constant number of bits set.
159
    ///
160
    /// This uses an iterative algorithm that selects one bit at a time
161
    /// using a u8 from the Rng for each, discarding duplicates.
162
    #[inline(always)]
163
161312
    fn select_constant_weight_bit_mask(&mut self, num_ones: usize) -> u32 {
164
161312
        let mut result = 0_u32;
165
161312
        let mut count = 0;
166
839646
        while count < num_ones {
167
678334
            let bit = 1 << ((self.rng.next_u8() as usize) % 32);
168
678334
            if (result & bit) == 0 {
169
645248
                result |= bit;
170
645248
                count += 1;
171
645248
            }
172
        }
173
161312
        result
174
161312
    }
175

            
176
    /// Generate random nonzero values.
177
    ///
178
    /// Iteratively picks a random u32, masking it, and discarding results that
179
    /// would be all zero.
180
    #[inline(always)]
181
1804394
    fn select_nonzero_u32(&mut self, mask: u32) -> u32 {
182
        loop {
183
1810997
            let result = self.rng.next_u32() & mask;
184
1810997
            if result != 0 {
185
1804394
                return result;
186
6603
            }
187
        }
188
1804394
    }
189

            
190
    /// Generate an entire program.
191
    ///
192
    /// Generates instructions into a provided [`Vec`] until the generator
193
    /// state can't be advanced any further. Runs the whole-program validator.
194
    /// Returns with [`Error::ProgramConstraints`] if the program fails these
195
    /// checks. This happens in normal use on a small fraction of seed values.
196
    #[inline(always)]
197
10082
    pub(crate) fn generate_program(&mut self, output: &mut InstructionVec) -> Result<(), Error> {
198
10082
        assert!(output.is_empty());
199
5158150
        while !output.is_full() {
200
5158150
            match self.generate_instruction() {
201
                Err(()) => break,
202
5158150
                Ok((inst, regw)) => {
203
5158150
                    let state_advance = self.commit_instruction_state(&inst, regw);
204
5158150
                    output.push(inst);
205
5158150
                    if let Err(()) = state_advance {
206
10082
                        break;
207
5148068
                    }
208
                }
209
            }
210
        }
211
10082
        self.validator
212
10082
            .check_whole_program(&self.scheduler, output)
213
10082
            .map_err(|()| Error::ProgramConstraints)
214
10082
    }
215

            
216
    /// Generate the next instruction.
217
    ///
218
    /// This is a multi-pass approach, starting with a [`Pass::Original`] that
219
    /// normally succeeds, followed by a [`Pass::Retry`] with simplifications
220
    /// to improve success rate, followed by a timing stall that advances the
221
    /// simulated cycle count forward and tries again with the benefit of newly
222
    /// available registers in the schedule.
223
    ///
224
    /// This only returns `Err(())` if we've hit a stopping condition for the
225
    /// program.
226
    #[inline(always)]
227
5158150
    fn generate_instruction(&mut self) -> Result<(Instruction, RegisterWriter), ()> {
228
        loop {
229
5159428
            if let Ok(result) = self.instruction_gen_attempt(Pass::Original) {
230
5155168
                return Ok(result);
231
4260
            }
232
4260
            if let Ok(result) = self.instruction_gen_attempt(Pass::Retry) {
233
2982
                return Ok(result);
234
1278
            }
235
1278
            self.scheduler.stall()?;
236
        }
237
5158150
    }
238

            
239
    /// Choose an opcode using the current [`OpcodeSelector`], subject to
240
    /// stateful constraints on adjacent opcode choices.
241
    #[inline(always)]
242
5163688
    fn choose_opcode(&mut self, pass: Pass) -> Opcode {
243
5163688
        let op = loop {
244
5520889
            let sub_cycle = self.scheduler.instruction_stream_sub_cycle();
245
5520889
            let op = model::choose_opcode_selector(pass, sub_cycle).apply(self);
246
5520889
            if let Ok(()) = constraints::opcode_pair_allowed(self.last_selector_result_op, op) {
247
5163688
                break op;
248
357201
            }
249
        };
250
5163688
        self.last_selector_result_op = Some(op);
251
5163688
        op
252
5163688
    }
253

            
254
    /// Make one attempt at instruction generation.
255
    ///
256
    /// This picks an [`OpcodeSelector`], chooses an opcode, then finishes
257
    /// choosing the opcode-specific parts of the instruction. Each of these
258
    /// choices affects the Rng state, and may fail if conditions are not met.
259
    #[inline(always)]
260
5163688
    fn instruction_gen_attempt(&mut self, pass: Pass) -> Result<(Instruction, RegisterWriter), ()> {
261
5163688
        let op = self.choose_opcode(pass);
262
5163688
        let plan = self.scheduler.instruction_plan(op)?;
263
5163688
        let (inst, regw) = self.choose_instruction_with_opcode_plan(op, pass, &plan)?;
264
5158150
        debug_assert_eq!(inst.opcode(), op);
265
5158150
        self.scheduler.commit_instruction_plan(&plan, &inst);
266
5158150
        Ok((inst, regw))
267
5163688
    }
268

            
269
    /// Choose only a source register, depending on the opcode and timing plan
270
    #[inline(never)]
271
3036670
    fn choose_src_reg(
272
3036670
        &mut self,
273
3036670
        op: Opcode,
274
3036670
        timing_plan: &InstructionPlan,
275
3036670
    ) -> Result<RegisterId, ()> {
276
24293360
        let src_set = RegisterSet::from_filter(|src| {
277
24293360
            self.scheduler
278
24293360
                .register_available(src, timing_plan.cycle_issued())
279
24293360
        });
280
3036670
        let src_set = constraints::src_registers_allowed(src_set, op);
281
3036670
        self.select_register(&src_set)
282
3036670
    }
283

            
284
    /// Choose both a source and destination register using a normal
285
    /// [`RegisterWriter`] for two-operand instructions.
286
    #[inline(always)]
287
2714046
    fn choose_src_dst_regs(
288
2714046
        &mut self,
289
2714046
        op: Opcode,
290
2714046
        pass: Pass,
291
2714046
        writer_info_fn: fn(RegisterId) -> RegisterWriter,
292
2714046
        timing_plan: &InstructionPlan,
293
2714046
    ) -> Result<(RegisterId, RegisterId, RegisterWriter), ()> {
294
2714046
        let src = self.choose_src_reg(op, timing_plan)?;
295
2714046
        let writer_info = writer_info_fn(src);
296
2714046
        let dst = self.choose_dst_reg(op, pass, writer_info, Some(src), timing_plan)?;
297
2708721
        Ok((src, dst, writer_info))
298
2714046
    }
299

            
300
    /// Choose both a source and destination register, with a custom
301
    /// [`RegisterWriter`] constraint that doesn't depend on source
302
    /// register choice.
303
    #[inline(always)]
304
322624
    fn choose_src_dst_regs_with_writer_info(
305
322624
        &mut self,
306
322624
        op: Opcode,
307
322624
        pass: Pass,
308
322624
        writer_info: RegisterWriter,
309
322624
        timing_plan: &InstructionPlan,
310
322624
    ) -> Result<(RegisterId, RegisterId), ()> {
311
322624
        let src = self.choose_src_reg(op, timing_plan)?;
312
322624
        let dst = self.choose_dst_reg(op, pass, writer_info, Some(src), timing_plan)?;
313
322624
        Ok((src, dst))
314
322624
    }
315

            
316
    /// Choose a destination register only, using source and writer info
317
    /// as well as the current state of the validator.
318
    #[inline(never)]
319
4841064
    fn choose_dst_reg(
320
4841064
        &mut self,
321
4841064
        op: Opcode,
322
4841064
        pass: Pass,
323
4841064
        writer_info: RegisterWriter,
324
4841064
        src: Option<RegisterId>,
325
4841064
        timing_plan: &InstructionPlan,
326
4841064
    ) -> Result<RegisterId, ()> {
327
4841064
        let validator = self
328
4841064
            .validator
329
4841064
            .dst_registers_allowed(op, pass, writer_info, src);
330
38728512
        let dst_set = RegisterSet::from_filter(|dst| {
331
38728512
            self.scheduler
332
38728512
                .register_available(dst, timing_plan.cycle_issued())
333
23942265
                && validator.check(dst)
334
38728512
        });
335
4841064
        self.select_register(&dst_set)
336
4841064
    }
337

            
338
    /// With an [`Opcode`] and an execution unit timing plan already in mind,
339
    /// generate the other pieces necessary to fully describe an
340
    /// [`Instruction`].
341
    ///
342
    /// This can fail if register selection fails.
343
    #[inline(always)]
344
5163688
    fn choose_instruction_with_opcode_plan(
345
5163688
        &mut self,
346
5163688
        op: Opcode,
347
5163688
        pass: Pass,
348
5163688
        plan: &InstructionPlan,
349
5163688
    ) -> Result<(Instruction, RegisterWriter), ()> {
350
5163688
        Ok(match op {
351
161312
            Opcode::Target => (Instruction::Target, RegisterWriter::None),
352

            
353
161312
            Opcode::Branch => (
354
161312
                Instruction::Branch {
355
161312
                    mask: self.select_constant_weight_bit_mask(model::BRANCH_MASK_BIT_WEIGHT),
356
161312
                },
357
161312
                RegisterWriter::None,
358
161312
            ),
359

            
360
            Opcode::UMulH => {
361
161667
                let regw = RegisterWriter::UMulH(self.rng.next_u32());
362
161667
                let (src, dst) = self.choose_src_dst_regs_with_writer_info(op, pass, regw, plan)?;
363
161667
                (Instruction::UMulH { src, dst }, regw)
364
            }
365

            
366
            Opcode::SMulH => {
367
160957
                let regw = RegisterWriter::SMulH(self.rng.next_u32());
368
160957
                let (src, dst) = self.choose_src_dst_regs_with_writer_info(op, pass, regw, plan)?;
369
160957
                (Instruction::SMulH { src, dst }, regw)
370
            }
371

            
372
            Opcode::Mul => {
373
1616741
                let regw = RegisterWriter::Mul;
374
1616741
                let (src, dst, regw) = self.choose_src_dst_regs(op, pass, regw, plan)?;
375
1611842
                (Instruction::Mul { src, dst }, regw)
376
            }
377

            
378
            Opcode::Sub => {
379
359118
                let regw = RegisterWriter::AddSub;
380
359118
                let (src, dst, regw) = self.choose_src_dst_regs(op, pass, regw, plan)?;
381
359118
                (Instruction::Sub { src, dst }, regw)
382
            }
383

            
384
            Opcode::Xor => {
385
390429
                let regw = RegisterWriter::Xor;
386
390429
                let (src, dst, regw) = self.choose_src_dst_regs(op, pass, regw, plan)?;
387
390429
                (Instruction::Xor { src, dst }, regw)
388
            }
389

            
390
            Opcode::AddShift => {
391
347758
                let regw = RegisterWriter::AddSub;
392
347758
                let left_shift = (self.rng.next_u32() & 3) as u8;
393
347758
                let (src, dst, regw) = self.choose_src_dst_regs(op, pass, regw, plan)?;
394
347332
                (
395
347332
                    Instruction::AddShift {
396
347332
                        src,
397
347332
                        dst,
398
347332
                        left_shift,
399
347332
                    },
400
347332
                    regw,
401
347332
                )
402
            }
403

            
404
            Opcode::AddConst => {
405
700912
                let regw = RegisterWriter::AddConst;
406
700912
                let src = self.select_nonzero_u32(u32::MAX) as i32;
407
700912
                let dst = self.choose_dst_reg(op, pass, regw, None, plan)?;
408
700770
                (Instruction::AddConst { src, dst }, regw)
409
            }
410

            
411
            Opcode::XorConst => {
412
709361
                let regw = RegisterWriter::XorConst;
413
709361
                let src = self.select_nonzero_u32(u32::MAX) as i32;
414
709361
                let dst = self.choose_dst_reg(op, pass, regw, None, plan)?;
415
709290
                (Instruction::XorConst { src, dst }, regw)
416
            }
417

            
418
            Opcode::Rotate => {
419
394121
                let regw = RegisterWriter::Rotate;
420
394121
                let right_rotate: u8 = self.select_nonzero_u32(63) as u8;
421
394121
                let dst = self.choose_dst_reg(op, pass, regw, None, plan)?;
422
394121
                (Instruction::Rotate { dst, right_rotate }, regw)
423
            }
424
        })
425
5163688
    }
426

            
427
    /// Commit all state modifications associated with a chosen instruction
428
    /// that's certainly being written to the final program.
429
    ///
430
    /// Returns `Ok(())` on success or `Err(())` if the new state would no
431
    /// longer be valid for program generation and we're done writing code.
432
    #[inline(always)]
433
5158150
    fn commit_instruction_state(
434
5158150
        &mut self,
435
5158150
        inst: &Instruction,
436
5158150
        regw: RegisterWriter,
437
5158150
    ) -> Result<(), ()> {
438
5158150
        self.validator.commit_instruction(inst, regw);
439
5158150
        self.scheduler.advance_instruction_stream(inst.opcode())
440
5158150
    }
441
}
442

            
443
/// HashX uses a limited number of different instruction selection strategies,
444
/// chosen based on the sub-cycle timing of our position in the
445
/// instruction stream.
446
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
447
enum OpcodeSelector {
448
    /// Main ALU instruction chooser, picks from Add/Sub/Xor/Rotate
449
    Normal,
450
    /// Retry pass if Normal fails, instructions with immediate source only
451
    ImmediateSrc,
452
    /// Only multiply instructions, no additional register selection work.
453
    Mul,
454
    /// Wide multiply instructions, randomly choosing signedness
455
    WideMul,
456
    /// Only branch targets
457
    Target,
458
    /// Only branch instructions
459
    Branch,
460
}
461

            
462
impl OpcodeSelector {
463
    /// Apply the selector, advancing the Rng state and returning an Opcode.
464
    #[inline(always)]
465
5520889
    fn apply<R: RngCore>(&self, generator: &mut Generator<'_, R>) -> Opcode {
466
5520889
        match self {
467
161312
            OpcodeSelector::Target => Opcode::Target,
468
161312
            OpcodeSelector::Branch => Opcode::Branch,
469
1616741
            OpcodeSelector::Mul => Opcode::Mul,
470
3258261
            OpcodeSelector::Normal => *generator.select_op(&model::NORMAL_OPS_TABLE),
471
639
            OpcodeSelector::ImmediateSrc => *generator.select_op(&model::IMMEDIATE_SRC_OPS_TABLE),
472
322624
            OpcodeSelector::WideMul => *generator.select_op(&model::WIDE_MUL_OPS_TABLE),
473
        }
474
5520889
    }
475
}