-
Notifications
You must be signed in to change notification settings - Fork 0
/
bp.rs
742 lines (619 loc) · 23.3 KB
/
bp.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
use perfect::*;
use perfect::events::*;
use perfect::stats::*;
use perfect::util::*;
use perfect::ir::branch::*;
use itertools::*;
use std::collections::*;
use bitvec::prelude::*;
fn gen_random_addr() -> usize {
let r = thread_rng().gen_range(0x2000..=0x4fff);
0x0000_0000_0000_0000usize | (r << 32)
}
#[derive(Clone, Copy)]
pub struct BranchRecord<const FOOTPRINT: usize> {
base_addr: usize,
idx: u8,
brn_off: usize,
tgt_off: usize,
}
impl <const FOOTPRINT: usize> BranchRecord<FOOTPRINT> {
const IDX_SHIFT: usize = 64 - (FOOTPRINT.leading_zeros() as usize) - 1;
pub fn new(base_addr: usize, idx: u8, brn_off: usize, tgt_off: usize)
-> Self
{
assert!(FOOTPRINT <= (1 << 25),
"BranchRecord footprint {:016x} is unreasonably large",
FOOTPRINT
);
// Just to make things less confusing..
assert!(FOOTPRINT.count_ones() == 1,
"BranchRecord footprint {:016x} has more than one set bit",
FOOTPRINT
);
assert!(idx <= 0xc0);
// Constrain placement within the footprint
assert!((brn_off & !(FOOTPRINT - 1) == 0),
"Branch offset {:016x} must be < {:016x}", brn_off,
FOOTPRINT
);
assert!((tgt_off & !(FOOTPRINT - 1) == 0),
"Branch target offset {:016x} must be < {:016x}", tgt_off,
FOOTPRINT,
);
// Only forward targets are allowed
assert!(tgt_off > brn_off,
"Branch target offset {:016x} must be < branch offset {:016x}",
tgt_off, brn_off
);
// The smallest branch/jmp encoding is two bytes
assert!((tgt_off - brn_off) >= 2,
"Difference between the branch target offset ({:016x}) and the \
branch offset ({:016x}) must be at least 2 bytes",
tgt_off, brn_off
);
Self {
base_addr,
idx,
brn_off: brn_off & (FOOTPRINT - 1),
tgt_off: tgt_off & (FOOTPRINT - 1),
}
}
pub fn addr(&self) -> usize {
self.base_addr | (self.idx as usize) << Self::IDX_SHIFT | self.brn_off
}
pub fn tgt(&self) -> usize {
self.base_addr | (self.idx as usize) << Self::IDX_SHIFT | self.tgt_off
}
}
pub struct BranchGroup<const FOOTPRINT: usize> {
base_addr: usize,
idx: u8,
padding: Vec<BranchRecord<FOOTPRINT>>,
a: Option<BranchRecord<FOOTPRINT>>,
b: Option<BranchRecord<FOOTPRINT>>,
}
impl <const FOOTPRINT: usize> BranchGroup<FOOTPRINT> {
pub fn new(base_addr: usize) -> Self {
Self {
base_addr,
idx: 0,
padding: Vec::new(),
a: None,
b: None,
}
}
pub fn add_padding_brn(&mut self, brn_off: usize, tgt_off: usize) {
//assert!(self.a.is_none() && self.b.is_none());
self.padding.push(BranchRecord::new(
self.base_addr, self.idx, brn_off, tgt_off
));
self.idx += 1;
}
pub fn def_brn_a(&mut self, brn_off: usize, tgt_off: usize) {
self.a = Some(BranchRecord::new(
self.base_addr, self.idx, brn_off, tgt_off
));
self.idx += 1;
}
pub fn def_brn_b(&mut self, brn_off: usize, tgt_off: usize) {
self.b = Some(BranchRecord::new(
self.base_addr, self.idx, brn_off, tgt_off
));
self.idx += 1;
}
pub fn pad_iter(&self) -> impl Iterator<Item=&BranchRecord<FOOTPRINT>> {
self.padding.iter()
}
pub fn brn_a(&self) -> BranchRecord<FOOTPRINT> {
if let Some(x) = self.a { x }
else { panic!("Branch 'A' is undefined"); }
}
pub fn brn_b(&self) -> BranchRecord<FOOTPRINT> {
if let Some(x) = self.b { x }
else { panic!("Branch 'B' is undefined"); }
}
pub fn print(&self) {
let first = self.padding.first().unwrap();
let last = self.padding.last().unwrap();
println!("pad_f: {:016x} {:016x}", first.addr(), first.tgt());
println!("pad_l: {:016x} {:016x}", last.addr(), last.tgt());
if let Some(a) = self.a {
println!("brn_a: {:016x} {:016x}", a.addr(), a.tgt());
}
if let Some(b) = self.b {
println!("brn_b: {:016x} {:016x}", b.addr(), b.tgt());
}
}
}
#[derive(Copy, Clone)]
pub struct ClearGhistArgs {
num_padding: usize,
pad_addr_off: usize,
pad_tgt_off: usize,
a_addr_off: usize,
a_tgt_off: usize,
b_addr_off: usize,
b_tgt_off: usize,
}
pub struct ClearGhist;
impl ClearGhist {
const NUM_ITER: usize = 1024;
const EVENT: Zen2Event = Zen2Event::ExRetMsprdBrnchInstrDirMsmtch(0x00);
fn emit_trampoline() -> X64AssemblerFixed {
let base_addr = gen_random_addr();
let mut f = X64AssemblerFixed::new(
base_addr,
0x0000_0000_0010_0000
);
f.emit_rdpmc_start(0, Gpr::R15 as u8);
dynasm!(f
; call rsi
);
f.emit_rdpmc_end(0, Gpr::R15 as u8, Gpr::Rax as u8);
f.emit_ret();
f.commit().unwrap();
f
}
fn emit<const FOOTPRINT: usize>(arg: ClearGhistArgs) -> X64AssemblerFixed
{
let base_addr = gen_random_addr();
let mut grp: BranchGroup<FOOTPRINT> = BranchGroup::new(base_addr);
for idx in 1..=arg.num_padding {
grp.add_padding_brn(
arg.pad_addr_off,
arg.pad_tgt_off,
);
}
grp.def_brn_a(
arg.a_addr_off,
arg.a_tgt_off,
);
grp.def_brn_b(
arg.b_addr_off,
arg.b_tgt_off,
);
//println!("FOOTPRINT: {:032b}", FOOTPRINT);
//println!("pad_addr_off: {:032b}", arg.pad_addr_off);
//println!("pad_tgt_off: {:032b}", arg.pad_tgt_off);
//println!("a_addr_off: {:032b}", arg.a_addr_off);
//println!("a_tgt_off: {:032b}", arg.a_tgt_off);
//println!("b_addr_off: {:032b}", arg.b_addr_off);
//println!("b_tgt_off: {:032b}", arg.b_tgt_off);
//grp.print();
let mut f = X64AssemblerFixed::new(
base_addr,
0x0000_0000_c000_0000
);
let brn_a = grp.brn_a();
let brn_b = grp.brn_b();
for pad_brn in grp.pad_iter() {
f.pad_until(pad_brn.addr());
let lab = f.new_dynamic_label();
let span = pad_brn.tgt() - pad_brn.addr();
if span < 128 {
dynasm!(f ; jmp BYTE =>lab);
} else {
dynasm!(f ; jmp =>lab);
}
f.pad_until(pad_brn.tgt());
f.place_dynamic_label(lab);
}
f.pad_until(brn_a.addr());
let lab_a = f.new_dynamic_label();
let span = brn_a.tgt() - brn_a.addr();
if span < 128 {
dynasm!(f ; je BYTE =>lab_a);
} else {
dynasm!(f ; je =>lab_a);
}
f.pad_until(brn_a.tgt());
f.place_dynamic_label(lab_a);
f.pad_until(brn_b.addr());
let lab_b = f.new_dynamic_label();
let span = brn_b.tgt() - brn_b.addr();
if span < 128 {
dynasm!(f ; je BYTE =>lab_b);
} else {
dynasm!(f ; je =>lab_b);
}
f.pad_until(brn_b.tgt());
f.place_dynamic_label(lab_b);
f.emit_ret();
f.commit().unwrap();
f
}
fn run(harness: &mut PerfectHarness) {
let desc = Self::EVENT.as_desc();
const FTPT: usize = 0b0_0000_0010_0000_0000_0000_0000;
let mut cases: Vec<ClearGhistArgs> = Vec::new();
//cases.push(ClearGhistArgs {
// num_padding: 91,
// pad_addr_off: 0b0_0000_0000_0000_0000_0000_0000,
// pad_tgt_off: 0b0_0000_0001_0000_0000_0000_0000,
// a_addr_off: 0b0_0000_0000_0000_0000_0000_0000,
// a_tgt_off: 0b0_0000_0001_0000_0000_0000_0000,
// b_addr_off: 0b0_0000_0000_0000_0000_0000_0000,
// b_tgt_off: 0b0_0000_0001_0000_0000_0000_0000,
//});
//for i in 0..=15 {
// cases.push(ClearGhistArgs {
// num_padding: 94,
// pad_addr_off: 0b0_0000_0000_0000_0000_0000_0000,
// pad_tgt_off: 0b0_0000_0001_0000_0000_0000_0000,
// a_addr_off: 0b0_0000_0000_0000_0000_0000_0000 | (1<<i),
// a_tgt_off: 0b0_0000_0001_0000_0000_0000_0000,
// b_addr_off: 0b0_0000_0000_0000_0000_0000_0000 | (1<<i),
// b_tgt_off: 0b0_0000_0001_0000_0000_0000_0000,
// });
//}
//for i in 0..=15 {
// cases.push(ClearGhistArgs {
// num_padding: 94,
// pad_addr_off: 0b0_0000_0000_0000_0000_0000_0000,
// pad_tgt_off: 0b0_0000_0001_0000_0000_0000_0000,
// a_addr_off: 0b0_0000_0000_0000_0000_0000_0000,
// a_tgt_off: 0b0_0000_0001_0000_0000_0000_0000 | (1<<i),
// b_addr_off: 0b0_0000_0000_0000_0000_0000_0000,
// b_tgt_off: 0b0_0000_0001_0000_0000_0000_0000 | (1<<i),
// });
//}
let trampoline = Self::emit_trampoline();
let trampoline_fn = trampoline.as_fn();
for (caseno, case) in cases.iter().enumerate() {
let f = Self::emit::<FTPT>(*case);
let func = f.as_fn();
let mut inputs: Vec<(usize,usize)> = (0..Self::NUM_ITER)
.map(|i| (thread_rng().gen::<bool>() as usize, func as usize))
.collect();
flush_btb::<8192>();
let results = harness.measure(trampoline_fn,
desc.id(), desc.mask(), Self::NUM_ITER,
InputMethod::List(&inputs)
).unwrap();
let dist = results.get_distribution();
let min = results.get_min();
let max = results.get_max();
let hitrate = results.count(0) as f32 / Self::NUM_ITER as f32;
println!(" hitrate={:.3} num_padding={:02} pad_addr_off={:016x} pad_tgt_off={:016x} \
a_addr_off={:016x} a_tgt_off={:016x} b_addr_off={:016x} b_tgt_off={:016x} dist={:?}",
hitrate,
case.num_padding,
case.pad_addr_off,
case.pad_tgt_off,
case.a_addr_off,
case.a_tgt_off,
case.b_addr_off,
case.b_tgt_off,
dist
);
}
}
}
/// [Naively?] try to interfere with two correlated conditional branches.
///
/// Context
/// =======
///
/// Predicting the *direction* of a branch usually entails the following:
///
/// - Keeping track of a "local" history of outcomes for a particular branch
/// - Keeping track of a "global" history of outcomes for all branches
/// - Using some fancy method of combining these two kinds of information
///
/// Test
/// ====
///
/// 1. Emit a branch 'A' with a *random* outcome.
/// 2. Emit a variable number of always-taken padding branches/jumps.
/// 3. Emit a branch 'B' with *the same* random outcome as branch 'A'.
///
/// Under normal circumstances (with very few padding branches in-between), we
/// expect that the branch predictor establishes a correlation between the
/// outcome of 'A' and the outcome of 'B'. This means that 'B' should be
/// correctly predicted very close to 100% of the time.
///
/// However, after a certain number of padding branches, we expect that the
/// machine will not be able to preserve the correlation between 'A' and 'B'.
///
/// This is a reasonable assumption because the amount of storage used for
/// tracking branch history must be finite. Eventually, the outcome of 'A'
/// will be lost [overwritten by the outcomes of the padding branches].
///
/// This should cause 'B' to be correctly predicted only ~50% of the time
/// (ie. the best you can do at predicting random outcomes, assuming you're
/// only using a "local" history of outcomes, or using a fixed prediction).
///
/// Results
/// =======
///
/// The prediction hit rate decreases to ~50% after 90 padding branches.
///
/// This probably reflects one (or both?) of the following things:
///
/// - We've filled up some [global] history register with taken outcomes,
/// which prevents the predictor from accessing the outcome of branch A.
/// - We've created aliasing in some table of tracked branches
///
/// .. although, this test doesn't tell us exactly which of these is the case.
/// The predictors are probably sensitive to the exact target address and
/// program counter of each branch (which we are sort of ignoring here).
///
pub struct CorrelatedBranches;
impl CorrelatedBranches {
const NUM_ITER: usize = 2048;
const EVENT: Zen2Event = Zen2Event::ExRetMsprdBrnchInstrDirMsmtch(0x00);
/// Emit the test.
///
/// Arguments
/// =========
///
/// - `num_padding`: The number of padding jumps
/// - `abit`: Requested alignment (in bits) for all branch addresses
/// and targets
/// - `a_brn_off`: Offset added to the branch address of 'A'
/// - `a_brn_tgt`: Offset added to the target address of 'A'
/// - `b_brn_off`: Offset added to the branch address of 'B'
/// - `b_brn_tgt`: Offset added to the target address of 'B'
///
fn emit(
num_padding: usize,
abit: usize,
a_brn_off: usize,
a_tgt_off: usize,
b_brn_off: usize,
b_tgt_off: usize,
) -> X64AssemblerFixed
{
let align = Align::from_bit(abit);
let mut f = X64AssemblerFixed::new(
gen_random_addr(),
0x0000_0000_8000_0000
);
// Emit branch 'A'.
//
// This branch is *always* predicted locally [assuming that we've
// successfully cleared the state of global history before this] and
// it should not be correlated with the outcome of a previous branch?
//
// NOTE: You can verify this by wrapping this block with RDPMC and
// observing that the misprediction rate is always 50%.
let cursor = AlignedAddress::new(f.cur_addr(), align);
let brn_a = BranchDesc::new(
cursor.aligned().value() + a_brn_off,
cursor.aligned().next().value() + a_tgt_off,
);
brn_a.emit_je_direct(&mut f);
// Emit a variable number of unconditional padding jumps.
let cursor = AlignedAddress::new(f.cur_addr(), align).next();
let set = BranchSet::gen_uniform_offset(
cursor.aligned().value(),
align,
0x0000_0000_0000_0000,
num_padding
);
for jmp in &set.data {
jmp.emit_jmp_direct(&mut f);
}
// Emit branch 'B', which will be measured with RDPMC.
//
// When a correlation with the first branch can be maintained, we
// should expect this to be correctly-predicted most if not all the
// time.
//
// NOTE: Since `emit_rdpmc_start()` occupies 0x18 bytes, we need the
// requested alignment to be at least 5 bits. Would be nice to find
// a way around this...
assert!(abit >= 5);
let cursor = AlignedAddress::new(f.cur_addr(), align)
.aligned()
.next();
let brn_b = BranchDesc::new(
cursor.value() + b_brn_off,
cursor.aligned().next().value() + b_tgt_off,
//cursor.value() + 2,
);
f.pad_until(brn_b.addr - 0x18);
f.emit_rdpmc_start(0, Gpr::R15 as u8);
brn_b.emit_je_direct(&mut f);
f.emit_rdpmc_end(0, Gpr::R15 as u8, Gpr::Rax as u8);
f.emit_ret();
f.commit().unwrap();
//println!(" brn_a: {:016x} => {:016x}", brn_a.addr, brn_a.tgt);
//for jmp in &set.data {
// println!(" pad : {:016x} => {:016x}", jmp.addr, jmp.tgt);
//}
////let last = set.last().unwrap();
////println!(" pad_t: {:016x} => {:016x}", last.addr, last.tgt);
//println!(" brn_b: {:016x} => {:016x}", brn_b.addr, brn_b.tgt);
f
}
fn run(harness: &mut PerfectHarness) {
let desc = Self::EVENT.as_desc();
let abit = 16;
let mut offsets = vec![0];
let mut x: Vec<usize> = (0..=abit-1).map(|x| 1 << x).collect();
offsets.append(&mut x);
for off in offsets {
for num_padding in 1..=96 {
let f = Self::emit(num_padding, abit, 0, 0, 0, off);
let func = f.as_fn();
// Try to reset the state of the predictor before entering each
// instance of the test.
flush_btb::<8192>();
let results = harness.measure(func,
desc.id(), desc.mask(), Self::NUM_ITER,
InputMethod::Random(&|rng, _| {
(rng.gen::<bool>() as usize, 0)
}),
).unwrap();
let dist = results.get_distribution();
let min = results.get_min();
let max = results.get_max();
let hitrate = results.count(0) as f32 / Self::NUM_ITER as f32;
println!("abit={:02} b_brn_off={:016x} padding={:03} hitrate={:.3}",
abit, off, num_padding, hitrate
);
}
}
}
fn run_abit_scan(harness: &mut PerfectHarness) {
let desc = Self::EVENT.as_desc();
// NOTE: Why does using a random order change the results?
let mut paddings = (1..=96).collect_vec();
//paddings.shuffle(&mut thread_rng());
for abit in 5..=19 {
let mut res = Vec::new();
//for num_padding in 1..=96 {
for num_padding in &paddings {
let f = Self::emit(*num_padding, abit, 0, 0, 0, 0);
let func = f.as_fn();
// Try to reset the state of the predictor before entering each
// instance of the test.
flush_btb::<8192>();
let results = harness.measure(func,
desc.id(), desc.mask(), Self::NUM_ITER,
InputMethod::Random(&|rng, _| {
(rng.gen::<bool>() as usize, 0)
}),
).unwrap();
let dist = results.get_distribution();
let min = results.get_min();
let max = results.get_max();
let hitrate = results.count(0) as f32 / Self::NUM_ITER as f32;
res.push(hitrate);
println!("abit={:02} padding={:03} hitrate={:.3}",
abit, num_padding, hitrate
);
}
}
}
}
/// Alternate version of [`CorrelatedBranches`], but with a single padding
/// branch taken in a loop some variable number of times.
///
/// In this case, the correlation is lost after repeating the branch 91 times.
/// Since the last outcome in the loop is necessarily "not-taken", this is
/// the exact same result as the test in [`CorrelatedBranches`], where we've
/// inserted 90 "taken" outcomes in-between branch 'A' and branch 'B'.
///
pub struct CorrelatedBranchesSimple;
impl CorrelatedBranchesSimple {
fn emit() -> X64AssemblerFixed {
let mut f = X64AssemblerFixed::new(
gen_random_addr(),
0x0000_0000_1000_0000
);
dynasm!(f
// Branch A
; ->brn_a:
; je ->brn_a_tgt
// Branch A target
; ->brn_a_tgt:
// Padding branch target
; ->pad_tgt:
; dec rsi
// Padding branch
; ->pad:
; jnz ->pad_tgt
; lfence
; mov rcx, 0
; rdpmc
; mov r15, rax
; lfence
; cmp rdi, 1
// Branch B
; ->brn_b:
; je ->brn_b_tgt
// Branch B target
; ->brn_b_tgt:
; lfence
; rdpmc
; lfence
; sub rax, r15
; ret
);
let brn_a = f.labels.resolve_static(
&StaticLabel::global("brn_a")
).unwrap();
let brn_a_tgt = f.labels.resolve_static(
&StaticLabel::global("brn_a_tgt")
).unwrap();
let pad = f.labels.resolve_static(
&StaticLabel::global("pad")
).unwrap();
let pad_tgt = f.labels.resolve_static(
&StaticLabel::global("pad_tgt")
).unwrap();
let brn_b = f.labels.resolve_static(
&StaticLabel::global("brn_b")
).unwrap();
let brn_b_tgt = f.labels.resolve_static(
&StaticLabel::global("brn_b_tgt")
).unwrap();
println!("brn_a: {:016x} => {:016x}",
brn_a.0 + f.base_addr(),
brn_a_tgt.0 + f.base_addr(),
);
println!("pad: {:016x} => {:016x}",
pad.0 + f.base_addr(),
pad_tgt.0 + f.base_addr(),
);
println!("brn_b: {:016x} => {:016x}",
brn_b.0 + f.base_addr(),
brn_b_tgt.0 + f.base_addr(),
);
f.commit().unwrap();
f
}
const NUM_ITER: usize = 4096;
const EVENT: Zen2Event = Zen2Event::ExRetMsprdBrnchInstrDirMsmtch(0x00);
fn run(harness: &mut PerfectHarness) {
let desc = Self::EVENT.as_desc();
let mut res = Vec::new();
for num_padding in 1..=96 {
let f = Self::emit();
let func = f.as_fn();
// Try to reset the state of the predictor before entering each
// instance of the test.
flush_btb::<8192>();
let inputs: Vec<(usize,usize)> = (0..Self::NUM_ITER)
.map(|i| (thread_rng().gen::<bool>() as usize, num_padding))
.collect();
let results = harness.measure(func,
desc.id(), desc.mask(), Self::NUM_ITER,
InputMethod::List(&inputs)
).unwrap();
let dist = results.get_distribution();
let min = results.get_min();
let max = results.get_max();
let hitrate = results.count(0) as f32 / Self::NUM_ITER as f32;
res.push(hitrate);
println!("num_padding={:2} min={} max={} hitrate={:.3} dist={:?}",
num_padding, min,max,hitrate,dist
);
}
// Form a bitstring representing each instance of the test, where '1'
// means that the correlation between the two branches was captured.
let pass: BitVec<usize, Msb0> = res.iter().map(|r| *r > 0.75)
.collect();
let chunk_sz = 8;
println!("[*] Test results (groups of {} tests)", chunk_sz);
for chunk in pass.chunks(chunk_sz) {
println!("{:0format$b}", chunk.load::<usize>(), format = chunk_sz);
}
}
}
fn main() {
let r = thread_rng().gen_range(0x1000..=0x1fff);
let harness_addr = 0x0000_0000_0000_0000usize | (r << 32);
let mut harness = HarnessConfig::default_zen2()
//.harness_addr(harness_addr)
.cmp_rdi(1)
.arena_alloc(0x0000_0000_0000_0000, 0x0000_0000_1000_0000)
.emit();
//CorrelatedBranchesSimple::run(&mut harness);
//CorrelatedBranches::run_abit_scan(&mut harness);
//CorrelatedBranches::run(&mut harness);
ClearGhist::run(&mut harness);
}