1#![allow(dead_code)]
57
58use crate::data_structures::{
59 BlockIx, InstIx, Map, RealReg, RealRegUniverse, Reg, RegSets, SpillSlot, VirtualReg, Writable,
60};
61use crate::inst_stream::{ExtPoint, InstExtPoint, InstToInsertAndExtPoint};
62use crate::{analysis_data_flow::get_san_reg_sets_for_insn, StackmapRequestInfo};
63use crate::{Function, RegUsageMapper};
64
65use rustc_hash::FxHashSet;
66use std::collections::VecDeque;
67use std::default::Default;
68use std::hash::Hash;
69use std::result::Result;
70
71use log::debug;
72
73#[derive(Clone, Debug)]
75pub struct CheckerErrors {
76 errors: Vec<CheckerError>,
77}
78
79#[derive(Clone, Debug)]
81pub enum CheckerError {
82 MissingAllocationForReg {
83 reg: VirtualReg,
84 inst: InstIx,
85 },
86 UnknownValueInReg {
87 real_reg: RealReg,
88 inst: InstIx,
89 },
90 IncorrectValueInReg {
91 actual: Reg,
92 expected: Reg,
93 real_reg: RealReg,
94 inst: InstIx,
95 },
96 UnknownValueInSlot {
97 slot: SpillSlot,
98 expected: Reg,
99 inst: InstIx,
100 },
101 IncorrectValueInSlot {
102 slot: SpillSlot,
103 expected: Reg,
104 actual: Reg,
105 inst: InstIx,
106 },
107 StackMapSpecifiesNonRefSlot {
108 inst: InstIx,
109 slot: SpillSlot,
110 },
111 StackMapSpecifiesUndefinedSlot {
112 inst: InstIx,
113 slot: SpillSlot,
114 },
115}
116
117#[derive(Clone, Copy, Debug, PartialEq, Eq)]
123enum CheckerValue {
124 Unknown,
126 Conflicted,
128 Reg(Reg, bool),
134}
135
136impl Default for CheckerValue {
137 fn default() -> CheckerValue {
138 CheckerValue::Unknown
139 }
140}
141
142impl CheckerValue {
143 fn meet(&self, other: &CheckerValue) -> CheckerValue {
145 match (self, other) {
146 (&CheckerValue::Unknown, _) => *other,
147 (_, &CheckerValue::Unknown) => *self,
148 (&CheckerValue::Conflicted, _) => *self,
149 (_, &CheckerValue::Conflicted) => *other,
150 (&CheckerValue::Reg(r1, ref1), &CheckerValue::Reg(r2, ref2)) if r1 == r2 => {
151 CheckerValue::Reg(r1, ref1 || ref2)
152 }
153 _ => CheckerValue::Conflicted,
154 }
155 }
156}
157
158#[derive(Clone, Debug, PartialEq, Eq)]
160struct CheckerState {
161 reg_values: Map<RealReg, CheckerValue>,
163 spill_slots: Map<SpillSlot, CheckerValue>,
165}
166
167impl Default for CheckerState {
168 fn default() -> CheckerState {
169 CheckerState {
170 reg_values: Map::default(),
171 spill_slots: Map::default(),
172 }
173 }
174}
175
176fn merge_map<K: Copy + Clone + PartialEq + Eq + Hash>(
177 into: &mut Map<K, CheckerValue>,
178 from: &Map<K, CheckerValue>,
179) {
180 for (k, v) in from {
181 let into_v = into.entry(*k).or_insert(Default::default());
182 let merged = into_v.meet(v);
183 *into_v = merged;
184 }
185}
186
187impl CheckerState {
188 fn new() -> CheckerState {
190 Default::default()
191 }
192
193 fn entry_state(ru: &RealRegUniverse) -> CheckerState {
195 let mut state = CheckerState::new();
196 for &(rreg, _) in &ru.regs {
197 state
198 .reg_values
199 .insert(rreg, CheckerValue::Reg(rreg.to_reg(), false));
200 }
201 state
202 }
203
204 fn meet_with(&mut self, other: &CheckerState) {
206 merge_map(&mut self.reg_values, &other.reg_values);
207 merge_map(&mut self.spill_slots, &other.spill_slots);
208 }
209
210 fn check(&self, inst: &Inst) -> Result<(), CheckerError> {
212 match inst {
213 &Inst::Op {
214 inst_ix,
215 ref uses_orig,
216 ref uses,
217 ..
218 } => {
219 assert!(uses_orig.len() == uses.len());
222 for (orig, mapped) in uses_orig.iter().cloned().zip(uses.iter().cloned()) {
223 let val = self
224 .reg_values
225 .get(&mapped)
226 .cloned()
227 .unwrap_or(Default::default());
228 debug!(
229 "checker: inst {:?}: orig {:?}, mapped {:?}, checker state {:?}",
230 inst, orig, mapped, val
231 );
232 match val {
233 CheckerValue::Unknown | CheckerValue::Conflicted => {
234 return Err(CheckerError::UnknownValueInReg {
235 real_reg: mapped,
236 inst: inst_ix,
237 });
238 }
239 CheckerValue::Reg(r, _) if r != orig => {
240 return Err(CheckerError::IncorrectValueInReg {
241 actual: r,
242 expected: orig,
243 real_reg: mapped,
244 inst: inst_ix,
245 });
246 }
247 _ => {}
248 }
249 }
250 }
251 &Inst::ChangeSpillSlotOwnership {
252 inst_ix,
253 slot,
254 from_reg,
255 ..
256 } => {
257 let val = self
258 .spill_slots
259 .get(&slot)
260 .cloned()
261 .unwrap_or(Default::default());
262 debug!("checker: inst {:?}: slot value {:?}", inst, val);
263 match val {
264 CheckerValue::Unknown | CheckerValue::Conflicted => {
265 return Err(CheckerError::UnknownValueInSlot {
266 slot,
267 expected: from_reg,
268 inst: inst_ix,
269 });
270 }
271 CheckerValue::Reg(r, _) if r != from_reg => {
272 return Err(CheckerError::IncorrectValueInSlot {
273 slot,
274 expected: from_reg,
275 actual: r,
276 inst: inst_ix,
277 });
278 }
279 _ => {}
280 }
281 }
282 &Inst::Safepoint { inst_ix, ref slots } => {
283 self.check_stackmap(inst_ix, slots)?;
284 }
285 _ => {}
286 }
287 Ok(())
288 }
289
290 fn check_stackmap(&self, inst: InstIx, slots: &Vec<SpillSlot>) -> Result<(), CheckerError> {
291 for &slot in slots {
295 match self.spill_slots.get(&slot) {
296 Some(CheckerValue::Reg(_, false)) => {
297 return Err(CheckerError::StackMapSpecifiesNonRefSlot { inst, slot });
298 }
299 Some(CheckerValue::Reg(_, true)) => {
300 }
302 _ => {
303 return Err(CheckerError::StackMapSpecifiesUndefinedSlot { inst, slot });
304 }
305 }
306 }
307 Ok(())
308 }
309
310 fn update_stackmap(&mut self, slots: &Vec<SpillSlot>) {
311 for (&slot, val) in &mut self.spill_slots {
312 if let &mut CheckerValue::Reg(_, true) = val {
313 let in_stackmap = slots.binary_search(&slot).is_ok();
314 if !in_stackmap {
315 *val = CheckerValue::Unknown;
316 }
317 }
318 }
319 for (_, val) in &mut self.reg_values {
322 if let &mut CheckerValue::Reg(_, true) = val {
323 *val = CheckerValue::Unknown;
324 }
325 }
326 }
327
328 fn update(&mut self, inst: &Inst) {
330 match inst {
331 &Inst::Op {
332 ref defs_orig,
333 ref defs,
334 ref defs_reftyped,
335 ..
336 } => {
337 assert!(defs_orig.len() == defs.len());
340 for i in 0..defs.len() {
341 let orig = defs_orig[i];
342 let mapped = defs[i];
343 let reftyped = defs_reftyped[i];
344 self.reg_values
345 .insert(mapped, CheckerValue::Reg(orig, reftyped));
346 }
347 }
348 &Inst::Move { into, from } => {
349 let val = self
350 .reg_values
351 .get(&from)
352 .cloned()
353 .unwrap_or(Default::default());
354 self.reg_values.insert(into.to_reg(), val);
355 }
356 &Inst::ChangeSpillSlotOwnership { slot, to_reg, .. } => {
357 let reftyped = if let Some(val) = self.spill_slots.get(&slot) {
358 match val {
359 &CheckerValue::Reg(_, reftyped) => reftyped,
360 _ => false,
361 }
362 } else {
363 false
364 };
365 self.spill_slots
366 .insert(slot, CheckerValue::Reg(to_reg, reftyped));
367 }
368 &Inst::Spill { into, from } => {
369 let val = self
370 .reg_values
371 .get(&from)
372 .cloned()
373 .unwrap_or(Default::default());
374 self.spill_slots.insert(into, val);
375 }
376 &Inst::Reload { into, from } => {
377 let val = self
378 .spill_slots
379 .get(&from)
380 .cloned()
381 .unwrap_or(Default::default());
382 self.reg_values.insert(into.to_reg(), val);
383 }
384 &Inst::Safepoint { ref slots, .. } => {
385 self.update_stackmap(slots);
386 }
387 }
388 }
389}
390
391#[derive(Clone, Debug)]
393pub(crate) enum Inst {
394 Spill { into: SpillSlot, from: RealReg },
396 Reload {
398 into: Writable<RealReg>,
399 from: SpillSlot,
400 },
401 Move {
403 into: Writable<RealReg>,
404 from: RealReg,
405 },
406 ChangeSpillSlotOwnership {
410 inst_ix: InstIx,
411 slot: SpillSlot,
412 from_reg: Reg,
413 to_reg: Reg,
414 },
415 Op {
418 inst_ix: InstIx,
419 defs_orig: Vec<Reg>,
420 uses_orig: Vec<Reg>,
421 defs: Vec<RealReg>,
422 uses: Vec<RealReg>,
423 defs_reftyped: Vec<bool>,
424 },
425 Safepoint {
427 inst_ix: InstIx,
428 slots: Vec<SpillSlot>,
429 },
430}
431
432#[derive(Debug)]
433pub(crate) struct Checker {
434 bb_entry: BlockIx,
435 bb_in: Map<BlockIx, CheckerState>,
436 bb_succs: Map<BlockIx, Vec<BlockIx>>,
437 bb_insts: Map<BlockIx, Vec<Inst>>,
438 reftyped_vregs: FxHashSet<VirtualReg>,
439 has_run: bool,
440}
441
442fn map_regs<F: Fn(VirtualReg) -> Option<RealReg>>(
443 inst: InstIx,
444 regs: &[Reg],
445 f: &F,
446) -> Result<Vec<RealReg>, CheckerErrors> {
447 let mut errors = Vec::new();
448 let real_regs = regs
449 .iter()
450 .map(|r| {
451 if r.is_virtual() {
452 f(r.to_virtual_reg()).unwrap_or_else(|| {
453 errors.push(CheckerError::MissingAllocationForReg {
454 reg: r.to_virtual_reg(),
455 inst,
456 });
457 Reg::new_real(r.get_class(), 0x0, 0).to_real_reg()
459 })
460 } else {
461 r.to_real_reg()
462 }
463 })
464 .collect();
465 if errors.is_empty() {
466 Ok(real_regs)
467 } else {
468 Err(CheckerErrors { errors })
469 }
470}
471
472impl Checker {
473 pub(crate) fn new<F: Function>(
477 f: &F,
478 ru: &RealRegUniverse,
479 reftyped_vregs: &[VirtualReg],
480 ) -> Checker {
481 let mut bb_in = Map::default();
482 let mut bb_succs = Map::default();
483 let mut bb_insts = Map::default();
484
485 for block in f.blocks() {
486 bb_in.insert(block, Default::default());
487 bb_succs.insert(block, f.block_succs(block).to_vec());
488 bb_insts.insert(block, vec![]);
489 }
490
491 bb_in.insert(f.entry_block(), CheckerState::entry_state(ru));
492
493 let reftyped_vregs = reftyped_vregs.iter().cloned().collect::<FxHashSet<_>>();
494 Checker {
495 bb_entry: f.entry_block(),
496 bb_in,
497 bb_succs,
498 bb_insts,
499 reftyped_vregs,
500 has_run: false,
501 }
502 }
503
504 pub(crate) fn add_inst(&mut self, block: BlockIx, inst: Inst) {
509 let insts = self.bb_insts.get_mut(&block).unwrap();
510 insts.push(inst);
511 }
512
513 pub(crate) fn add_op<RUM: RegUsageMapper>(
518 &mut self,
519 block: BlockIx,
520 inst_ix: InstIx,
521 regsets: &RegSets,
522 mapper: &RUM,
523 ) -> Result<(), CheckerErrors> {
524 debug!(
525 "add_op: block {} inst {} regsets {:?}",
526 block.get(),
527 inst_ix.get(),
528 regsets
529 );
530 assert!(regsets.is_sanitized());
531 let mut uses_set = regsets.uses.clone();
532 let mut defs_set = regsets.defs.clone();
533 uses_set.union(®sets.mods);
534 defs_set.union(®sets.mods);
535 if uses_set.is_empty() && defs_set.is_empty() {
536 return Ok(());
537 }
538
539 let uses_orig = uses_set.to_vec();
540 let defs_orig = defs_set.to_vec();
541 let uses = map_regs(inst_ix, &uses_orig[..], &|vreg| mapper.get_use(vreg))?;
542 let defs = map_regs(inst_ix, &defs_orig[..], &|vreg| mapper.get_def(vreg))?;
543 let defs_reftyped = defs_orig
544 .iter()
545 .map(|reg| reg.is_virtual() && self.reftyped_vregs.contains(®.to_virtual_reg()))
546 .collect();
547 let insts = self.bb_insts.get_mut(&block).unwrap();
548 let op = Inst::Op {
549 inst_ix,
550 uses_orig,
551 defs_orig,
552 uses,
553 defs,
554 defs_reftyped,
555 };
556 debug!("add_op: adding {:?}", op);
557 insts.push(op);
558 Ok(())
559 }
560
561 fn analyze(&mut self) {
563 let mut queue = VecDeque::new();
564 queue.push_back(self.bb_entry);
565
566 while !queue.is_empty() {
567 let block = queue.pop_front().unwrap();
568 let mut state = self.bb_in.get(&block).cloned().unwrap();
569 debug!("analyze: block {} has state {:?}", block.get(), state);
570 for inst in self.bb_insts.get(&block).unwrap() {
571 state.update(inst);
572 debug!("analyze: inst {:?} -> state {:?}", inst, state);
573 }
574
575 for succ in self.bb_succs.get(&block).unwrap() {
576 let cur_succ_in = self.bb_in.get(succ).unwrap();
577 let mut new_state = state.clone();
578 new_state.meet_with(cur_succ_in);
579 let changed = &new_state != cur_succ_in;
580 if changed {
581 debug!(
582 "analyze: block {} state changed from {:?} to {:?}; pushing onto queue",
583 succ.get(),
584 cur_succ_in,
585 new_state
586 );
587 self.bb_in.insert(*succ, new_state);
588 queue.push_back(*succ);
589 }
590 }
591 }
592 }
593
594 fn find_errors(&self) -> Result<(), CheckerErrors> {
598 let mut errors = vec![];
599 for (block, input) in &self.bb_in {
600 let mut state = input.clone();
601 for inst in self.bb_insts.get(block).unwrap() {
602 if let Err(e) = state.check(inst) {
603 debug!("Checker error: {:?}", e);
604 errors.push(e);
605 }
606 state.update(inst);
607 }
608 }
609
610 if errors.is_empty() {
611 Ok(())
612 } else {
613 Err(CheckerErrors { errors })
614 }
615 }
616
617 pub(crate) fn run(mut self) -> Result<(), CheckerErrors> {
620 debug!("Checker: full body is:\n{:?}", self.bb_insts);
621 self.has_run = true;
622 self.analyze();
623 self.find_errors()
624 }
625}
626
627pub(crate) struct CheckerContext {
630 checker: Checker,
631 checker_inst_map: Map<InstExtPoint, Vec<Inst>>,
632}
633
634pub(crate) struct CheckerStackmapInfo<'a> {
636 pub(crate) request: &'a StackmapRequestInfo,
637 pub(crate) stackmaps: &'a [Vec<SpillSlot>],
638}
639
640impl CheckerContext {
641 pub(crate) fn new<F: Function>(
644 f: &F,
645 ru: &RealRegUniverse,
646 insts_to_add: &Vec<InstToInsertAndExtPoint>,
647 stackmap_info: Option<CheckerStackmapInfo>,
648 ) -> CheckerContext {
649 let mut checker_inst_map: Map<InstExtPoint, Vec<Inst>> = Map::default();
650 for &InstToInsertAndExtPoint { ref inst, ref iep } in insts_to_add {
651 let checker_insts = checker_inst_map
652 .entry(iep.clone())
653 .or_insert_with(|| vec![]);
654 checker_insts.push(inst.to_checker_inst());
655 }
656
657 let reftyped_vregs = if let Some(info) = stackmap_info {
658 assert!(info.request.safepoint_insns.len() == info.stackmaps.len());
659 for (iix, slots) in info
660 .request
661 .safepoint_insns
662 .iter()
663 .zip(info.stackmaps.iter())
664 {
665 let iep = InstExtPoint::new(*iix, ExtPoint::Use);
666 let mut slots = slots.clone();
667 slots.sort();
668 checker_inst_map
669 .entry(iep)
670 .or_insert_with(|| vec![])
671 .push(Inst::Safepoint {
672 inst_ix: *iix,
673 slots,
674 });
675 }
676 info.request.reftyped_vregs.as_slice()
677 } else {
678 &[]
679 };
680
681 let checker = Checker::new(f, ru, reftyped_vregs);
682 CheckerContext {
683 checker,
684 checker_inst_map,
685 }
686 }
687
688 pub(crate) fn handle_insn<F: Function, RUM: RegUsageMapper>(
691 &mut self,
692 ru: &RealRegUniverse,
693 func: &F,
694 bix: BlockIx,
695 iix: InstIx,
696 mapper: &RUM,
697 ) -> Result<(), CheckerErrors> {
698 let empty = vec![];
699 let mut skip_inst = false;
700
701 debug!("CheckerContext::handle_insn: inst {:?}", iix,);
702
703 for &pre_point in &[ExtPoint::Reload, ExtPoint::SpillBefore, ExtPoint::Use] {
704 let pre_point = InstExtPoint::new(iix, pre_point);
705 for checker_inst in self.checker_inst_map.get(&pre_point).unwrap_or(&empty) {
706 debug!("at inst {:?}: pre checker_inst: {:?}", iix, checker_inst);
707 self.checker.add_inst(bix, checker_inst.clone());
708 if let Inst::ChangeSpillSlotOwnership { .. } = checker_inst {
709 skip_inst = true;
712 }
713 }
714 }
715
716 if !skip_inst {
717 let regsets = get_san_reg_sets_for_insn::<F>(func.get_insn(iix), ru)
718 .expect("only existing real registers at this point");
719 assert!(regsets.is_sanitized());
720
721 debug!(
722 "at inst {:?}: regsets {:?} mapper {:?}",
723 iix, regsets, mapper
724 );
725 self.checker.add_op(bix, iix, ®sets, mapper)?;
726 }
727
728 for &post_point in &[ExtPoint::ReloadAfter, ExtPoint::Spill] {
729 let post_point = InstExtPoint::new(iix, post_point);
730 for checker_inst in self.checker_inst_map.get(&post_point).unwrap_or(&empty) {
731 debug!("at inst {:?}: post checker_inst: {:?}", iix, checker_inst);
732 self.checker.add_inst(bix, checker_inst.clone());
733 }
734 }
735
736 Ok(())
737 }
738
739 pub(crate) fn run(self) -> Result<(), CheckerErrors> {
741 self.checker.run()
742 }
743}
744
745impl Drop for Checker {
746 fn drop(&mut self) {
747 if !self.has_run {
748 panic!("Programmer error: the CheckerContext run() function hasn't been called");
749 }
750 }
751}