1
//! Define HashX's register file, and how it's created and digested.
2

            
3
use crate::siphash::{SipState, siphash24_ctr};
4
use arrayvec::ArrayVec;
5
use std::fmt;
6

            
7
/// Number of virtual registers in the HashX machine
8
pub(crate) const NUM_REGISTERS: usize = 8;
9

            
10
/// Register `R5`
11
///
12
/// Most HashX registers have no special properties, so we don't even
13
/// bother naming them. Register R5 is the exception, HashX defines a
14
/// specific constraint there for the benefit of x86_64 code generation.
15
pub(crate) const R5: RegisterId = RegisterId(5);
16

            
17
/// Identify one register (R0 - R7) in HashX's virtual machine
18
#[derive(Clone, Copy, Eq, PartialEq)]
19
pub(crate) struct RegisterId(u8);
20

            
21
impl fmt::Debug for RegisterId {
22
110050
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23
110050
        write!(f, "R{}", self.0)
24
110050
    }
25
}
26

            
27
impl RegisterId {
28
    /// Cast this RegisterId into a plain usize
29
    #[inline(always)]
30
98258310
    pub(crate) fn as_usize(&self) -> usize {
31
98258310
        self.0 as usize
32
98258310
    }
33

            
34
    /// Return the underlying u8 for this RegisterId.
35
    ///
36
    /// (Recall that hashx has 8 virtual registers,
37
    /// so the output of this method is always in range 0..=7.)
38
    #[inline(always)]
39
    #[cfg(feature = "compiler")]
40
14960268
    pub(crate) fn as_u8(&self) -> u8 {
41
14960268
        self.0
42
14960268
    }
43

            
44
    /// Create an iterator over all RegisterId
45
    #[inline(always)]
46
8006256
    pub(crate) fn all() -> impl Iterator<Item = RegisterId> {
47
8006256
        (0_u8..(NUM_REGISTERS as u8)).map(RegisterId)
48
8006256
    }
49
}
50

            
51
/// Identify a set of RegisterIds
52
///
53
/// This could be done compactly as a u8 bitfield for storage purposes, but
54
/// in our program generator this is never stored long-term. Instead, we want
55
/// something the optimizer can reason about as effectively as possible, and
56
/// we want to optimize for an index() implementation that doesn't branch.
57
/// This uses a fixed-capacity array of registers in-set, always sorted.
58
#[derive(Default, Clone, Eq, PartialEq)]
59
pub(crate) struct RegisterSet(ArrayVec<RegisterId, NUM_REGISTERS>);
60

            
61
impl fmt::Debug for RegisterSet {
62
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63
        write!(f, "[")?;
64
        for n in 0..self.len() {
65
            if n != 0 {
66
                write!(f, ",")?;
67
            }
68
            self.index(n).fmt(f)?;
69
        }
70
        write!(f, "]")
71
    }
72
}
73

            
74
impl RegisterSet {
75
    /// Number of registers still contained in this set
76
    #[inline(always)]
77
8185320
    pub(crate) fn len(&self) -> usize {
78
8185320
        self.0.len()
79
8185320
    }
80

            
81
    /// Test if a register is contained in the set.
82
    #[inline(always)]
83
347758
    pub(crate) fn contains(&self, id: RegisterId) -> bool {
84
347758
        self.0.contains(&id)
85
347758
    }
86

            
87
    /// Build a new RegisterSet from each register for which a predicate
88
    /// function returns `true`.
89
    #[inline(always)]
90
7877734
    pub(crate) fn from_filter<P: FnMut(RegisterId) -> bool>(mut predicate: P) -> Self {
91
7877734
        let mut result: Self = Default::default();
92
63021872
        for r in RegisterId::all() {
93
63021872
            if predicate(r) {
94
33341245
                result.0.push(r);
95
33341245
            }
96
        }
97
7877734
        result
98
7877734
    }
99

            
100
    /// Return a particular register within this set, counting from R0 to R7.
101
    ///
102
    /// The supplied index must be less than the [`Self::len()`] of this set.
103
    /// Panics if the index is out of range.
104
    #[inline(always)]
105
7983072
    pub(crate) fn index(&self, index: usize) -> RegisterId {
106
7983072
        self.0[index]
107
7983072
    }
108
}
109

            
110
/// Values for all registers in the HashX machine
111
///
112
/// Guaranteed to have a `repr(C)` layout that includes each register in order
113
/// with no padding and no extra fields. The compiled runtime will produce
114
/// functions that read or write a `RegisterFile` directly.
115

            
116
#[derive(Debug, Clone, Eq, PartialEq)]
117
#[repr(C)]
118
pub(crate) struct RegisterFile([u64; NUM_REGISTERS]);
119

            
120
impl RegisterFile {
121
    /// Load a word from the register file.
122
    #[inline(always)]
123
223508
    pub(crate) fn load(&self, id: RegisterId) -> u64 {
124
223508
        self.0[id.as_usize()]
125
223508
    }
126

            
127
    /// Store a word into the register file.
128
    #[inline(always)]
129
138450
    pub(crate) fn store(&mut self, id: RegisterId, value: u64) {
130
138450
        self.0[id.as_usize()] = value;
131
138450
    }
132

            
133
    /// Initialize a new HashX register file, given a key (derived from
134
    /// the seed) and the user-specified hash input word.
135
    #[inline(always)]
136
349062270
    pub(crate) fn new(key: SipState, input: u64) -> Self {
137
349062270
        RegisterFile(siphash24_ctr(key, input))
138
349062270
    }
139

            
140
    /// Finalize the state of the register file and generate up to 4 words of
141
    /// output in HashX's final result format.
142
    ///
143
    /// This splits the register file into two halves, mixes in the siphash
144
    /// keys again to "remove bias toward 0 caused by multiplications", and
145
    /// runs one siphash round on each half before recombining them.
146
    #[inline(always)]
147
349062270
    pub(crate) fn digest(&self, key: SipState) -> [u64; 4] {
148
349062270
        let mut x = SipState {
149
349062270
            v0: self.0[0].wrapping_add(key.v0),
150
349062270
            v1: self.0[1].wrapping_add(key.v1),
151
349062270
            v2: self.0[2],
152
349062270
            v3: self.0[3],
153
349062270
        };
154
349062270
        let mut y = SipState {
155
349062270
            v0: self.0[4],
156
349062270
            v1: self.0[5],
157
349062270
            v2: self.0[6].wrapping_add(key.v2),
158
349062270
            v3: self.0[7].wrapping_add(key.v3),
159
349062270
        };
160
349062270
        x.sip_round();
161
349062270
        y.sip_round();
162
349062270
        [x.v0 ^ y.v0, x.v1 ^ y.v1, x.v2 ^ y.v2, x.v3 ^ y.v3]
163
349062270
    }
164
}