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