hara_native/jit/
hotness.rs1use std::collections::HashMap;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4pub struct LoopKey {
5 pub function: u16,
6 pub header: u32,
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct JitConfig {
11 pub hot_threshold: u32,
12 pub side_trace_threshold: u32,
13 pub max_traces_per_loop: usize,
14 pub max_branch_exits_before_bailout: u32,
15 pub min_iterations_per_branch_exit: u32,
16 pub max_trace_operations: usize,
17}
18
19impl Default for JitConfig {
20 fn default() -> Self {
21 Self {
22 hot_threshold: 16,
23 side_trace_threshold: 16,
24 max_traces_per_loop: 4,
25 max_branch_exits_before_bailout: 32,
26 min_iterations_per_branch_exit: 8,
27 max_trace_operations: 4096,
28 }
29 }
30}
31
32#[derive(Debug)]
33pub struct Hotness {
34 config: JitConfig,
35 counters: HashMap<LoopKey, u32>,
36}
37
38impl Hotness {
39 pub fn new(config: JitConfig) -> Self {
40 Self {
41 config,
42 counters: HashMap::new(),
43 }
44 }
45
46 pub fn backedge(&mut self, key: LoopKey) -> bool {
47 let counter = self.counters.entry(key).or_default();
48 *counter = counter.saturating_add(1);
49 *counter == self.config.hot_threshold
50 }
51
52 pub fn count(&self, key: LoopKey) -> u32 {
53 self.counters.get(&key).copied().unwrap_or(0)
54 }
55
56 pub fn config(&self) -> JitConfig {
57 self.config
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn counters_are_per_loop_and_saturating() {
67 let key = LoopKey {
68 function: 2,
69 header: 7,
70 };
71 let mut hotness = Hotness::new(JitConfig {
72 hot_threshold: 2,
73 side_trace_threshold: 2,
74 max_traces_per_loop: 4,
75 max_branch_exits_before_bailout: 32,
76 min_iterations_per_branch_exit: 8,
77 max_trace_operations: 10,
78 });
79 assert!(!hotness.backedge(key));
80 assert!(hotness.backedge(key));
81 assert!(!hotness.backedge(key));
82 assert_eq!(hotness.count(key), 3);
83 }
84}