1#![allow(dead_code)]
97
98use crate::{
99 Allocation, AllocationKind, Block, Edit, Function, FxHashMap, FxHashSet, Inst, InstOrEdit,
100 InstPosition, MachineEnv, Operand, OperandConstraint, OperandKind, OperandPos, Output, PReg,
101 PRegSet, VReg,
102};
103use alloc::vec::Vec;
104use alloc::{format, vec};
105use core::default::Default;
106use core::hash::Hash;
107use core::result::Result;
108use smallvec::{smallvec, SmallVec};
109
110#[derive(Clone, Debug)]
112pub struct CheckerErrors {
113 errors: Vec<CheckerError>,
114}
115
116#[derive(Clone, Debug)]
118pub enum CheckerError {
119 MissingAllocation {
120 inst: Inst,
121 op: Operand,
122 },
123 UnknownValueInAllocation {
124 inst: Inst,
125 op: Operand,
126 alloc: Allocation,
127 },
128 ConflictedValueInAllocation {
129 inst: Inst,
130 op: Operand,
131 alloc: Allocation,
132 },
133 IncorrectValuesInAllocation {
134 inst: Inst,
135 op: Operand,
136 alloc: Allocation,
137 actual: FxHashSet<VReg>,
138 },
139 ConstraintViolated {
140 inst: Inst,
141 op: Operand,
142 alloc: Allocation,
143 },
144 AllocationIsNotReg {
145 inst: Inst,
146 op: Operand,
147 alloc: Allocation,
148 },
149 AllocationIsNotFixedReg {
150 inst: Inst,
151 op: Operand,
152 alloc: Allocation,
153 },
154 AllocationIsNotReuse {
155 inst: Inst,
156 op: Operand,
157 alloc: Allocation,
158 expected_alloc: Allocation,
159 },
160 AllocationIsNotStack {
161 inst: Inst,
162 op: Operand,
163 alloc: Allocation,
164 },
165 StackToStackMove {
166 into: Allocation,
167 from: Allocation,
168 },
169}
170
171#[derive(Clone, Debug, PartialEq, Eq)]
177enum CheckerValue {
178 Universe,
181 VRegs(FxHashSet<VReg>),
183}
184
185impl CheckerValue {
186 fn vregs(&self) -> Option<&FxHashSet<VReg>> {
187 match self {
188 CheckerValue::Universe => None,
189 CheckerValue::VRegs(vregs) => Some(vregs),
190 }
191 }
192
193 fn vregs_mut(&mut self) -> Option<&mut FxHashSet<VReg>> {
194 match self {
195 CheckerValue::Universe => None,
196 CheckerValue::VRegs(vregs) => Some(vregs),
197 }
198 }
199}
200
201impl Default for CheckerValue {
202 fn default() -> CheckerValue {
203 CheckerValue::Universe
204 }
205}
206
207impl CheckerValue {
208 fn meet_with(&mut self, other: &CheckerValue) {
212 match (self, other) {
213 (_, CheckerValue::Universe) => {
214 }
216 (this @ CheckerValue::Universe, _) => {
217 *this = other.clone();
218 }
219 (CheckerValue::VRegs(my_vregs), CheckerValue::VRegs(other_vregs)) => {
220 my_vregs.retain(|vreg| other_vregs.contains(vreg));
221 }
222 }
223 }
224
225 fn from_reg(reg: VReg) -> CheckerValue {
226 CheckerValue::VRegs(core::iter::once(reg).collect())
227 }
228
229 fn remove_vreg(&mut self, reg: VReg) {
230 match self {
231 CheckerValue::Universe => {
232 panic!("Cannot remove VReg from Universe set (we do not have the full list of vregs available");
233 }
234 CheckerValue::VRegs(vregs) => {
235 vregs.remove(®);
236 }
237 }
238 }
239
240 fn copy_vreg(&mut self, src: VReg, dst: VReg) {
241 match self {
242 CheckerValue::Universe => {
243 }
245 CheckerValue::VRegs(vregs) => {
246 if vregs.contains(&src) {
247 vregs.insert(dst);
248 }
249 }
250 }
251 }
252
253 fn empty() -> CheckerValue {
254 CheckerValue::VRegs(FxHashSet::default())
255 }
256}
257
258fn visit_all_vregs<F: Function, V: FnMut(VReg)>(f: &F, mut v: V) {
259 for block in 0..f.num_blocks() {
260 let block = Block::new(block);
261 for inst in f.block_insns(block).iter() {
262 for op in f.inst_operands(inst) {
263 v(op.vreg());
264 }
265 if f.is_branch(inst) {
266 for succ_idx in 0..f.block_succs(block).len() {
267 for ¶m in f.branch_blockparams(block, inst, succ_idx) {
268 v(param);
269 }
270 }
271 }
272 }
273 for &vreg in f.block_params(block) {
274 v(vreg);
275 }
276 }
277}
278
279#[derive(Clone, Debug, PartialEq, Eq)]
281enum CheckerState {
282 Top,
283 Allocations(FxHashMap<Allocation, CheckerValue>),
284}
285
286impl CheckerState {
287 fn get_value(&self, alloc: &Allocation) -> Option<&CheckerValue> {
288 match self {
289 CheckerState::Top => None,
290 CheckerState::Allocations(allocs) => allocs.get(alloc),
291 }
292 }
293
294 fn get_values_mut(&mut self) -> impl Iterator<Item = &mut CheckerValue> {
295 match self {
296 CheckerState::Top => panic!("Cannot get mutable values iterator on Top state"),
297 CheckerState::Allocations(allocs) => allocs.values_mut(),
298 }
299 }
300
301 fn get_mappings(&self) -> impl Iterator<Item = (&Allocation, &CheckerValue)> {
302 match self {
303 CheckerState::Top => panic!("Cannot get mappings iterator on Top state"),
304 CheckerState::Allocations(allocs) => allocs.iter(),
305 }
306 }
307
308 fn get_mappings_mut(&mut self) -> impl Iterator<Item = (&Allocation, &mut CheckerValue)> {
309 match self {
310 CheckerState::Top => panic!("Cannot get mutable mappings iterator on Top state"),
311 CheckerState::Allocations(allocs) => allocs.iter_mut(),
312 }
313 }
314
315 fn become_defined(&mut self) {
317 match self {
318 CheckerState::Top => *self = CheckerState::Allocations(FxHashMap::default()),
319 _ => {}
320 }
321 }
322
323 fn set_value(&mut self, alloc: Allocation, value: CheckerValue) {
324 match self {
325 CheckerState::Top => {
326 panic!("Cannot set value on Top state");
327 }
328 CheckerState::Allocations(allocs) => {
329 allocs.insert(alloc, value);
330 }
331 }
332 }
333
334 fn copy_vreg(&mut self, src: VReg, dst: VReg) {
335 match self {
336 CheckerState::Top => {
337 }
339 CheckerState::Allocations(allocs) => {
340 for value in allocs.values_mut() {
341 value.copy_vreg(src, dst);
342 }
343 }
344 }
345 }
346
347 fn remove_value(&mut self, alloc: &Allocation) {
348 match self {
349 CheckerState::Top => {
350 panic!("Cannot remove value on Top state");
351 }
352 CheckerState::Allocations(allocs) => {
353 allocs.remove(alloc);
354 }
355 }
356 }
357
358 fn initial() -> Self {
359 CheckerState::Allocations(FxHashMap::default())
360 }
361}
362
363impl Default for CheckerState {
364 fn default() -> CheckerState {
365 CheckerState::Top
366 }
367}
368
369impl core::fmt::Display for CheckerValue {
370 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
371 match self {
372 CheckerValue::Universe => {
373 write!(f, "top")
374 }
375 CheckerValue::VRegs(vregs) => {
376 write!(f, "{{ ")?;
377 for vreg in vregs {
378 write!(f, "{} ", vreg)?;
379 }
380 write!(f, "}}")?;
381 Ok(())
382 }
383 }
384 }
385}
386
387fn merge_map<K: Copy + Clone + PartialEq + Eq + Hash>(
392 into: &mut FxHashMap<K, CheckerValue>,
393 from: &FxHashMap<K, CheckerValue>,
394) {
395 into.retain(|k, _| from.contains_key(k));
396 for (k, into_v) in into.iter_mut() {
397 let from_v = from.get(k).unwrap();
398 into_v.meet_with(from_v);
399 }
400}
401
402impl CheckerState {
403 fn new() -> CheckerState {
405 Default::default()
406 }
407
408 fn meet_with(&mut self, other: &CheckerState) {
410 match (self, other) {
411 (_, CheckerState::Top) => {
412 }
414 (this @ CheckerState::Top, _) => {
415 *this = other.clone();
416 }
417 (
418 CheckerState::Allocations(my_allocations),
419 CheckerState::Allocations(other_allocations),
420 ) => {
421 merge_map(my_allocations, other_allocations);
422 }
423 }
424 }
425
426 fn check_val<'a, F: Function>(
427 &self,
428 inst: Inst,
429 op: Operand,
430 alloc: Allocation,
431 val: &CheckerValue,
432 allocs: &[Allocation],
433 checker: &Checker<'a, F>,
434 ) -> Result<(), CheckerError> {
435 if alloc == Allocation::none() {
436 return Err(CheckerError::MissingAllocation { inst, op });
437 }
438
439 if op.kind() == OperandKind::Use && op.as_fixed_nonallocatable().is_none() {
440 match val {
441 CheckerValue::Universe => {
442 return Err(CheckerError::UnknownValueInAllocation { inst, op, alloc });
443 }
444 CheckerValue::VRegs(vregs) if !vregs.contains(&op.vreg()) => {
445 return Err(CheckerError::IncorrectValuesInAllocation {
446 inst,
447 op,
448 alloc,
449 actual: vregs.clone(),
450 });
451 }
452 _ => {}
453 }
454 }
455
456 self.check_constraint(inst, op, alloc, allocs, checker)?;
457
458 Ok(())
459 }
460
461 fn check<'a, F: Function>(
465 &self,
466 pos: InstPosition,
467 checkinst: &CheckerInst,
468 checker: &Checker<'a, F>,
469 ) -> Result<(), CheckerError> {
470 let default_val = Default::default();
471 match checkinst {
472 &CheckerInst::Op {
473 inst,
474 ref operands,
475 ref allocs,
476 ..
477 } => {
478 let has_reused_input = operands
482 .iter()
483 .any(|op| matches!(op.constraint(), OperandConstraint::Reuse(_)));
484 if has_reused_input && pos == InstPosition::After {
485 return Ok(());
486 }
487
488 for (op, alloc) in operands.iter().zip(allocs.iter()) {
492 let is_here = match (op.pos(), pos) {
493 (OperandPos::Early, InstPosition::Before) => true,
494 (OperandPos::Late, InstPosition::After) => true,
495 _ => false,
496 };
497 if !is_here {
498 continue;
499 }
500
501 let val = self.get_value(alloc).unwrap_or(&default_val);
502 trace!(
503 "checker: checkinst {:?}: op {:?}, alloc {:?}, checker value {:?}",
504 checkinst,
505 op,
506 alloc,
507 val
508 );
509 self.check_val(inst, *op, *alloc, val, allocs, checker)?;
510 }
511 }
512 &CheckerInst::Move { into, from } => {
513 let is_stack = |alloc: Allocation| {
515 if let Some(reg) = alloc.as_reg() {
516 checker.stack_pregs.contains(reg)
517 } else {
518 alloc.is_stack()
519 }
520 };
521 if is_stack(into) && is_stack(from) {
522 return Err(CheckerError::StackToStackMove { into, from });
523 }
524 }
525 &CheckerInst::ParallelMove { .. } => {
526 }
530 }
531 Ok(())
532 }
533
534 fn update(&mut self, checkinst: &CheckerInst) {
536 self.become_defined();
537
538 match checkinst {
539 &CheckerInst::Move { into, from } => {
540 if let Some(val) = self.get_value(&from).cloned() {
547 trace!(
548 "checker: checkinst {:?} updating: move {:?} -> {:?} val {:?}",
549 checkinst,
550 from,
551 into,
552 val
553 );
554 self.set_value(into, val);
555 }
556 }
557 &CheckerInst::ParallelMove { ref moves } => {
558 let mut additions: FxHashMap<VReg, SmallVec<[VReg; 2]>> = FxHashMap::default();
565 let mut deletions: FxHashSet<VReg> = FxHashSet::default();
566
567 for &(dest, src) in moves {
568 deletions.insert(dest);
569 additions
570 .entry(src)
571 .or_insert_with(|| smallvec![])
572 .push(dest);
573 }
574
575 for value in self.get_values_mut() {
580 if let Some(vregs) = value.vregs_mut() {
581 let mut insertions: SmallVec<[VReg; 2]> = smallvec![];
582 for &vreg in vregs.iter() {
583 if let Some(additions) = additions.get(&vreg) {
584 insertions.extend(additions.iter().cloned());
585 }
586 }
587 for &d in &deletions {
588 vregs.remove(&d);
589 }
590 vregs.extend(insertions);
591 }
592 }
593 }
594 &CheckerInst::Op {
595 ref operands,
596 ref allocs,
597 ref clobbers,
598 ..
599 } => {
600 for (op, alloc) in operands.iter().zip(allocs.iter()) {
605 if op.kind() != OperandKind::Def {
606 continue;
607 }
608 self.remove_vreg(op.vreg());
609 self.set_value(*alloc, CheckerValue::from_reg(op.vreg()));
610 }
611 for clobber in clobbers {
612 self.remove_value(&Allocation::reg(*clobber));
613 }
614 }
615 }
616 }
617
618 fn remove_vreg(&mut self, vreg: VReg) {
619 for (_, value) in self.get_mappings_mut() {
620 value.remove_vreg(vreg);
621 }
622 }
623
624 fn check_constraint<'a, F: Function>(
625 &self,
626 inst: Inst,
627 op: Operand,
628 alloc: Allocation,
629 allocs: &[Allocation],
630 checker: &Checker<'a, F>,
631 ) -> Result<(), CheckerError> {
632 match op.constraint() {
633 OperandConstraint::Any => {}
634 OperandConstraint::Reg => {
635 if let Some(preg) = alloc.as_reg() {
636 if !checker.machine_env.fixed_stack_slots.contains(&preg) {
638 return Ok(());
639 }
640 }
641 return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
642 }
643 OperandConstraint::Stack => {
644 if alloc.kind() != AllocationKind::Stack {
645 if let Some(preg) = alloc.as_reg() {
647 if checker.machine_env.fixed_stack_slots.contains(&preg) {
648 return Ok(());
649 }
650 }
651 return Err(CheckerError::AllocationIsNotStack { inst, op, alloc });
652 }
653 }
654 OperandConstraint::FixedReg(preg) => {
655 if alloc != Allocation::reg(preg) {
656 return Err(CheckerError::AllocationIsNotFixedReg { inst, op, alloc });
657 }
658 }
659 OperandConstraint::Reuse(idx) => {
660 if alloc.kind() != AllocationKind::Reg {
661 return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
662 }
663 if alloc != allocs[idx] {
664 return Err(CheckerError::AllocationIsNotReuse {
665 inst,
666 op,
667 alloc,
668 expected_alloc: allocs[idx],
669 });
670 }
671 }
672 }
673 Ok(())
674 }
675}
676
677#[derive(Clone, Debug)]
679pub(crate) enum CheckerInst {
680 Move { into: Allocation, from: Allocation },
683
684 ParallelMove {
689 moves: Vec<(VReg, VReg)>,
691 },
692
693 Op {
697 inst: Inst,
698 operands: Vec<Operand>,
699 allocs: Vec<Allocation>,
700 clobbers: Vec<PReg>,
701 },
702}
703
704#[derive(Debug)]
705pub struct Checker<'a, F: Function> {
706 f: &'a F,
707 bb_in: FxHashMap<Block, CheckerState>,
708 bb_insts: FxHashMap<Block, Vec<CheckerInst>>,
709 edge_insts: FxHashMap<(Block, Block), Vec<CheckerInst>>,
710 machine_env: &'a MachineEnv,
711 stack_pregs: PRegSet,
712}
713
714impl<'a, F: Function> Checker<'a, F> {
715 pub fn new(f: &'a F, machine_env: &'a MachineEnv) -> Checker<'a, F> {
720 let mut bb_in = FxHashMap::default();
721 let mut bb_insts = FxHashMap::default();
722 let mut edge_insts = FxHashMap::default();
723
724 for block in 0..f.num_blocks() {
725 let block = Block::new(block);
726 bb_in.insert(block, Default::default());
727 bb_insts.insert(block, vec![]);
728 for &succ in f.block_succs(block) {
729 edge_insts.insert((block, succ), vec![]);
730 }
731 }
732
733 bb_in.insert(f.entry_block(), CheckerState::default());
734
735 let mut stack_pregs = PRegSet::empty();
736 for &preg in &machine_env.fixed_stack_slots {
737 stack_pregs.add(preg);
738 }
739
740 Checker {
741 f,
742 bb_in,
743 bb_insts,
744 edge_insts,
745 machine_env,
746 stack_pregs,
747 }
748 }
749
750 pub fn prepare(&mut self, out: &Output) {
753 trace!("checker: out = {:?}", out);
754 let mut last_inst = None;
755 for block in 0..self.f.num_blocks() {
756 let block = Block::new(block);
757 for inst_or_edit in out.block_insts_and_edits(self.f, block) {
758 match inst_or_edit {
759 InstOrEdit::Inst(inst) => {
760 debug_assert!(last_inst.is_none() || inst > last_inst.unwrap());
761 last_inst = Some(inst);
762 self.handle_inst(block, inst, out);
763 }
764 InstOrEdit::Edit(edit) => self.handle_edit(block, edit),
765 }
766 }
767 }
768 }
769
770 fn handle_inst(&mut self, block: Block, inst: Inst, out: &Output) {
772 let operands: Vec<_> = self.f.inst_operands(inst).iter().cloned().collect();
774 let allocs: Vec<_> = out.inst_allocs(inst).iter().cloned().collect();
775 let clobbers: Vec<_> = self.f.inst_clobbers(inst).into_iter().collect();
776 let checkinst = CheckerInst::Op {
777 inst,
778 operands,
779 allocs,
780 clobbers,
781 };
782 trace!("checker: adding inst {:?}", checkinst);
783 self.bb_insts.get_mut(&block).unwrap().push(checkinst);
784
785 if self.f.is_branch(inst) {
788 for (i, &succ) in self.f.block_succs(block).iter().enumerate() {
789 let args = self.f.branch_blockparams(block, inst, i);
790 let params = self.f.block_params(succ);
791 assert_eq!(
792 args.len(),
793 params.len(),
794 "block{} has succ block{}; gave {} args for {} params",
795 block.index(),
796 succ.index(),
797 args.len(),
798 params.len()
799 );
800 if args.len() > 0 {
801 let moves = params.iter().cloned().zip(args.iter().cloned()).collect();
802 self.edge_insts
803 .get_mut(&(block, succ))
804 .unwrap()
805 .push(CheckerInst::ParallelMove { moves });
806 }
807 }
808 }
809 }
810
811 fn handle_edit(&mut self, block: Block, edit: &Edit) {
812 trace!("checker: adding edit {:?}", edit);
813 match edit {
814 &Edit::Move { from, to } => {
815 self.bb_insts
816 .get_mut(&block)
817 .unwrap()
818 .push(CheckerInst::Move { into: to, from });
819 }
820 }
821 }
822
823 fn analyze(&mut self) {
825 let mut queue = Vec::new();
826 let mut queue_set = FxHashSet::default();
827
828 for block in (0..self.f.num_blocks()).rev() {
837 let block = Block::new(block);
838 queue.push(block);
839 queue_set.insert(block);
840 }
841
842 while let Some(block) = queue.pop() {
843 queue_set.remove(&block);
844 let mut state = self.bb_in.get(&block).cloned().unwrap();
845 trace!("analyze: block {} has state {:?}", block.index(), state);
846 for inst in self.bb_insts.get(&block).unwrap() {
847 state.update(inst);
848 trace!("analyze: inst {:?} -> state {:?}", inst, state);
849 }
850
851 for &succ in self.f.block_succs(block) {
852 let mut new_state = state.clone();
853 for edge_inst in self.edge_insts.get(&(block, succ)).unwrap() {
854 new_state.update(edge_inst);
855 trace!(
856 "analyze: succ {:?}: inst {:?} -> state {:?}",
857 succ,
858 edge_inst,
859 new_state
860 );
861 }
862
863 let cur_succ_in = self.bb_in.get(&succ).unwrap();
864 trace!(
865 "meeting state {:?} for block {} with state {:?} for block {}",
866 new_state,
867 block.index(),
868 cur_succ_in,
869 succ.index()
870 );
871 new_state.meet_with(cur_succ_in);
872 let changed = &new_state != cur_succ_in;
873 trace!(" -> {:?}, changed {}", new_state, changed);
874
875 if changed {
876 trace!(
877 "analyze: block {} state changed from {:?} to {:?}; pushing onto queue",
878 succ.index(),
879 cur_succ_in,
880 new_state
881 );
882 self.bb_in.insert(succ, new_state);
883 if queue_set.insert(succ) {
884 queue.push(succ);
885 }
886 }
887 }
888 }
889 }
890
891 fn find_errors(&self) -> Result<(), CheckerErrors> {
895 let mut errors = vec![];
896 for (block, input) in &self.bb_in {
897 let mut state = input.clone();
898 for inst in self.bb_insts.get(block).unwrap() {
899 if let Err(e) = state.check(InstPosition::Before, inst, self) {
900 trace!("Checker error: {:?}", e);
901 errors.push(e);
902 }
903 state.update(inst);
904 if let Err(e) = state.check(InstPosition::After, inst, self) {
905 trace!("Checker error: {:?}", e);
906 errors.push(e);
907 }
908 }
909 }
910
911 if errors.is_empty() {
912 Ok(())
913 } else {
914 Err(CheckerErrors { errors })
915 }
916 }
917
918 pub fn run(mut self) -> Result<(), CheckerErrors> {
921 self.analyze();
922 let result = self.find_errors();
923
924 trace!("=== CHECKER RESULT ===");
925 fn print_state(state: &CheckerState) {
926 if !trace_enabled!() {
927 return;
928 }
929 if let CheckerState::Allocations(allocs) = state {
930 let mut s = vec![];
931 for (alloc, state) in allocs {
932 s.push(format!("{} := {}", alloc, state));
933 }
934 trace!(" {{ {} }}", s.join(", "))
935 }
936 }
937 for bb in 0..self.f.num_blocks() {
938 let bb = Block::new(bb);
939 trace!("block{}:", bb.index());
940 let insts = self.bb_insts.get(&bb).unwrap();
941 let mut state = self.bb_in.get(&bb).unwrap().clone();
942 print_state(&state);
943 for inst in insts {
944 match inst {
945 &CheckerInst::Op {
946 inst,
947 ref operands,
948 ref allocs,
949 ref clobbers,
950 } => {
951 trace!(
952 " inst{}: {:?} ({:?}) clobbers:{:?}",
953 inst.index(),
954 operands,
955 allocs,
956 clobbers
957 );
958 }
959 &CheckerInst::Move { from, into } => {
960 trace!(" {} -> {}", from, into);
961 }
962 &CheckerInst::ParallelMove { .. } => {
963 panic!("unexpected parallel_move in body (non-edge)")
964 }
965 }
966 state.update(inst);
967 print_state(&state);
968 }
969
970 for &succ in self.f.block_succs(bb) {
971 trace!(" succ {:?}:", succ);
972 let mut state = state.clone();
973 for edge_inst in self.edge_insts.get(&(bb, succ)).unwrap() {
974 match edge_inst {
975 &CheckerInst::ParallelMove { ref moves } => {
976 let moves = moves
977 .iter()
978 .map(|(dest, src)| format!("{} -> {}", src, dest))
979 .collect::<Vec<_>>();
980 trace!(" parallel_move {}", moves.join(", "));
981 }
982 _ => panic!("unexpected edge_inst: not a parallel move"),
983 }
984 state.update(edge_inst);
985 print_state(&state);
986 }
987 }
988 }
989
990 result
991 }
992}