2 error::{Error, Result},
3 index::{BlockIdx, InstIdx, InstRange, SSAValIdx},
4 interned::{GlobalState, Intern, Interned},
5 loc::{BaseTy, Loc, Ty},
8 use arbitrary::Arbitrary;
11 use hashbrown::HashSet;
14 visit::{GraphBase, GraphProp, IntoNeighbors, VisitMap, Visitable},
17 use serde::{Deserialize, Serialize};
18 use smallvec::SmallVec;
20 collections::{btree_map, BTreeMap, BTreeSet},
22 ops::{Index, IndexMut},
25 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
27 BlockParam { block: BlockIdx, param_idx: usize },
28 Operand { inst: InstIdx, operand_idx: usize },
31 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
32 pub struct BranchSuccParamUse {
33 pub branch_inst: InstIdx,
38 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
39 pub struct OperandUse {
41 pub operand_idx: usize,
44 #[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
48 pub operand_uses: BTreeSet<OperandUse>,
49 pub branch_succ_param_uses: BTreeSet<BranchSuccParamUse>,
53 fn validate(&self, ssa_val_idx: SSAValIdx, func: &FnFields) -> Result<()> {
58 branch_succ_param_uses,
61 SSAValDef::BlockParam { block, param_idx } => {
62 let block_param = func.try_get_block_param(block, param_idx)?;
63 if ssa_val_idx != block_param {
64 return Err(Error::MismatchedBlockParamDef {
71 SSAValDef::Operand { inst, operand_idx } => {
72 let operand = func.try_get_operand(inst, operand_idx)?;
73 if ssa_val_idx != operand.ssa_val {
74 return Err(Error::SSAValDefIsNotOperandsSSAVal {
82 for &OperandUse { inst, operand_idx } in operand_uses {
83 let operand = func.try_get_operand(inst, operand_idx)?;
84 if ssa_val_idx != operand.ssa_val {
85 return Err(Error::SSAValUseIsNotOperandsSSAVal {
92 for &BranchSuccParamUse {
96 } in branch_succ_param_uses
98 if ssa_val_idx != func.try_get_branch_target_param(branch_inst, succ, param_idx)? {
99 return Err(Error::MismatchedBranchTargetBlockParamUse {
131 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
132 #[serde(try_from = "SerializedProgPoint", into = "SerializedProgPoint")]
133 pub struct ProgPoint(usize);
136 pub const fn new(inst: InstIdx, stage: InstStage) -> Self {
137 const_unwrap_res!(Self::try_new(inst, stage))
139 pub const fn try_new(inst: InstIdx, stage: InstStage) -> Result<Self> {
140 let Some(inst) = inst.get().checked_shl(1) else {
141 return Err(Error::InstIdxTooBig);
143 Ok(Self(inst | stage as usize))
145 pub const fn inst(self) -> InstIdx {
146 InstIdx::new(self.0 >> 1)
148 pub const fn stage(self) -> InstStage {
155 pub const fn next(self) -> Self {
158 pub const fn prev(self) -> Self {
163 impl fmt::Debug for ProgPoint {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 f.debug_struct("ProgPoint")
166 .field("inst", &self.inst())
167 .field("stage", &self.stage())
172 #[derive(Serialize, Deserialize)]
173 struct SerializedProgPoint {
178 impl From<ProgPoint> for SerializedProgPoint {
179 fn from(value: ProgPoint) -> Self {
182 stage: value.stage(),
187 impl TryFrom<SerializedProgPoint> for ProgPoint {
190 fn try_from(value: SerializedProgPoint) -> Result<Self, Self::Error> {
191 ProgPoint::try_new(value.inst, value.stage)
210 pub enum OperandKind {
216 Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize, Arbitrary,
218 pub enum Constraint {
219 /// any register or stack location
223 /// r2,r4,r6,r8,...r126
229 /// any stack location
235 pub fn is_any(&self) -> bool {
236 matches!(self, Self::Any)
238 pub fn fixed_loc(&self) -> Option<Loc> {
241 | Constraint::BaseGpr
242 | Constraint::SVExtra2VGpr
243 | Constraint::SVExtra2SGpr
244 | Constraint::SVExtra3Gpr
245 | Constraint::Stack => None,
246 Constraint::FixedLoc(v) => Some(v),
249 pub fn non_fixed_choices_for_ty(ty: Ty) -> &'static [Constraint] {
250 match (ty.base_ty, ty.reg_len.get()) {
251 (BaseTy::Bits64, 1) => &[
254 Constraint::SVExtra2SGpr,
255 Constraint::SVExtra2VGpr,
256 Constraint::SVExtra3Gpr,
259 (BaseTy::Bits64, _) => &[
261 Constraint::SVExtra2VGpr,
262 Constraint::SVExtra3Gpr,
265 (BaseTy::Ca, _) | (BaseTy::VlMaxvl, _) => &[Constraint::Any, Constraint::Stack],
268 pub fn arbitrary_with_ty(
270 u: &mut arbitrary::Unstructured<'_>,
271 ) -> arbitrary::Result<Self> {
272 let non_fixed_choices = Self::non_fixed_choices_for_ty(ty);
273 if let Some(&retval) = non_fixed_choices.get(u.choose_index(non_fixed_choices.len() + 1)?) {
276 Ok(Constraint::FixedLoc(Loc::arbitrary_with_ty(ty, u)?))
279 pub fn check_for_ty_mismatch(&self, ty: Ty) -> Result<(), ()> {
281 Constraint::Any | Constraint::Stack => {}
282 Constraint::BaseGpr | Constraint::SVExtra2SGpr => {
283 if ty != Ty::scalar(BaseTy::Bits64) {
287 Constraint::SVExtra2VGpr | Constraint::SVExtra3Gpr => {
288 if ty.base_ty != BaseTy::Bits64 {
292 Constraint::FixedLoc(loc) => {
302 impl Default for Constraint {
303 fn default() -> Self {
309 Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize, Default,
311 #[serde(try_from = "OperandKind", into = "OperandKind")]
312 pub struct OperandKindDefOnly;
314 impl TryFrom<OperandKind> for OperandKindDefOnly {
317 fn try_from(value: OperandKind) -> Result<Self, Self::Error> {
319 OperandKind::Use => Err(Error::OperandKindMustBeDef),
320 OperandKind::Def => Ok(Self),
325 impl From<OperandKindDefOnly> for OperandKind {
326 fn from(_value: OperandKindDefOnly) -> Self {
331 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
333 pub enum KindAndConstraint {
335 kind: OperandKindDefOnly,
336 reuse_operand_idx: usize,
340 #[serde(default, skip_serializing_if = "Constraint::is_any")]
341 constraint: Constraint,
345 impl KindAndConstraint {
346 pub fn kind(self) -> OperandKind {
348 Self::Reuse { .. } => OperandKind::Def,
349 Self::Constraint { kind, .. } => kind,
352 pub fn is_reuse(self) -> bool {
353 matches!(self, Self::Reuse { .. })
357 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
359 pub ssa_val: SSAValIdx,
361 pub kind_and_constraint: KindAndConstraint,
362 pub stage: InstStage,
366 pub fn try_get_reuse_src<'f>(
370 ) -> Result<Option<&'f Operand>> {
371 if let KindAndConstraint::Reuse {
372 reuse_operand_idx, ..
373 } = self.kind_and_constraint
375 Ok(Some(func.try_get_operand(inst, reuse_operand_idx)?))
380 pub fn try_constraint(&self, inst: InstIdx, func: &FnFields) -> Result<Constraint> {
381 Ok(match self.kind_and_constraint {
382 KindAndConstraint::Reuse {
386 let operand = func.try_get_operand(inst, reuse_operand_idx)?;
387 match operand.kind_and_constraint {
388 KindAndConstraint::Reuse { .. }
389 | KindAndConstraint::Constraint {
390 kind: OperandKind::Def,
393 return Err(Error::ReuseTargetOperandMustBeUse {
395 reuse_target_operand_idx: reuse_operand_idx,
398 KindAndConstraint::Constraint {
399 kind: OperandKind::Use,
404 KindAndConstraint::Constraint { constraint, .. } => constraint,
407 pub fn constraint(&self, inst: InstIdx, func: &Function) -> Constraint {
408 self.try_constraint(inst, func).unwrap()
416 global_state: &GlobalState,
419 ssa_val: ssa_val_idx,
423 let ssa_val = func.try_get_ssa_val(ssa_val_idx)?;
424 match kind_and_constraint.kind() {
425 OperandKind::Use => {
428 .contains(&OperandUse { inst, operand_idx })
430 return Err(Error::MissingOperandUse {
437 OperandKind::Def => {
438 let def = SSAValDef::Operand { inst, operand_idx };
439 if ssa_val.def != def {
440 return Err(Error::OperandDefIsNotSSAValDef {
448 if let KindAndConstraint::Reuse {
451 } = self.kind_and_constraint
453 let reuse_src = func.try_get_operand(inst, reuse_operand_idx)?;
454 let reuse_src_ssa_val = func.try_get_ssa_val(reuse_src.ssa_val)?;
455 if ssa_val.ty != reuse_src_ssa_val.ty {
456 return Err(Error::ReuseOperandTyMismatch {
458 tgt_operand_idx: operand_idx,
459 src_operand_idx: reuse_operand_idx,
460 src_ty: reuse_src_ssa_val.ty,
465 let constraint = self.try_constraint(inst, func)?;
467 .check_for_ty_mismatch(ssa_val.ty)
468 .map_err(|()| Error::ConstraintTyMismatch {
473 if let Some(fixed_loc) = constraint.fixed_loc() {
478 .conflicts_with(fixed_loc, global_state)
480 return Err(Error::FixedLocConflictsWithClobbers { inst, operand_idx });
487 /// copy concatenates all `srcs` together and de-concatenates the result into all `dests`.
488 #[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
489 pub struct CopyInstKind {
490 pub src_operand_idxs: Vec<usize>,
491 pub dest_operand_idxs: Vec<usize>,
496 fn calc_copy_ty(operand_idxs: &[usize], inst: InstIdx, func: &FnFields) -> Result<Option<Ty>> {
497 let mut retval: Option<Ty> = None;
498 for &operand_idx in operand_idxs {
499 let operand = func.try_get_operand(inst, operand_idx)?;
500 let ssa_val = func.try_get_ssa_val(operand.ssa_val)?;
501 retval = Some(match retval {
502 Some(retval) => retval.try_concat(ssa_val.ty)?,
510 #[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
511 pub struct BlockTermInstKind {
512 pub succs_and_params: BTreeMap<BlockIdx, Vec<SSAValIdx>>,
515 #[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
519 BlockTerm(BlockTermInstKind),
523 pub fn is_normal(&self) -> bool {
524 matches!(self, Self::Normal)
526 pub fn is_block_term(&self) -> bool {
527 matches!(self, Self::BlockTerm { .. })
529 pub fn is_copy(&self) -> bool {
530 matches!(self, Self::Copy { .. })
532 pub fn block_term(&self) -> Option<&BlockTermInstKind> {
534 InstKind::BlockTerm(v) => Some(v),
538 pub fn block_term_mut(&mut self) -> Option<&mut BlockTermInstKind> {
540 InstKind::BlockTerm(v) => Some(v),
544 pub fn copy(&self) -> Option<&CopyInstKind> {
546 InstKind::Copy(v) => Some(v),
552 impl Default for InstKind {
553 fn default() -> Self {
558 fn loc_set_is_empty(clobbers: &Interned<LocSet>) -> bool {
562 fn empty_loc_set() -> Interned<LocSet> {
563 GlobalState::get(|global_state| LocSet::default().into_interned(global_state))
566 #[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
568 #[serde(default, skip_serializing_if = "InstKind::is_normal")]
570 pub operands: Vec<Operand>,
571 #[serde(default = "empty_loc_set", skip_serializing_if = "loc_set_is_empty")]
572 pub clobbers: Interned<LocSet>,
581 global_state: &GlobalState,
588 let is_at_end_of_block = func.blocks[block].insts.last() == Some(inst);
589 if kind.is_block_term() != is_at_end_of_block {
590 return Err(if is_at_end_of_block {
591 Error::BlocksLastInstMustBeTerm { term_idx: inst }
593 Error::TermInstOnlyAllowedAtBlockEnd { inst_idx: inst }
596 for (idx, operand) in operands.iter().enumerate() {
597 operand.validate(block, inst, idx, func, global_state)?;
600 InstKind::Normal => {}
601 InstKind::Copy(CopyInstKind {
606 let mut seen_dest_operands = SmallVec::<[bool; 16]>::new();
607 seen_dest_operands.resize(operands.len(), false);
608 for &dest_operand_idx in dest_operand_idxs {
609 let seen_dest_operand = seen_dest_operands.get_mut(dest_operand_idx).ok_or(
610 Error::OperandIndexOutOfRange {
612 operand_idx: dest_operand_idx,
615 if mem::replace(seen_dest_operand, true) {
616 return Err(Error::DupCopyDestOperand {
618 operand_idx: dest_operand_idx,
622 if Some(*copy_ty) != CopyInstKind::calc_copy_ty(&src_operand_idxs, inst, func)? {
623 return Err(Error::CopySrcTyMismatch { inst });
625 if Some(*copy_ty) != CopyInstKind::calc_copy_ty(&dest_operand_idxs, inst, func)? {
626 return Err(Error::CopyDestTyMismatch { inst });
629 InstKind::BlockTerm(BlockTermInstKind { succs_and_params }) => {
630 for (&succ_idx, params) in succs_and_params {
631 let succ = func.try_get_block(succ_idx)?;
632 if !succ.preds.contains(&block) {
633 return Err(Error::SrcBlockMissingFromBranchTgtBlocksPreds {
639 if succ.params.len() != params.len() {
640 return Err(Error::BranchSuccParamCountMismatch {
643 block_param_count: succ.params.len(),
644 branch_param_count: params.len(),
647 for (param_idx, (&branch_ssa_val_idx, &block_ssa_val_idx)) in
648 params.iter().zip(&succ.params).enumerate()
650 let branch_ssa_val = func.try_get_ssa_val(branch_ssa_val_idx)?;
651 let block_ssa_val = func.try_get_ssa_val(block_ssa_val_idx)?;
653 .branch_succ_param_uses
654 .contains(&BranchSuccParamUse {
660 return Err(Error::MissingBranchSuccParamUse {
661 ssa_val_idx: branch_ssa_val_idx,
667 if block_ssa_val.ty != branch_ssa_val.ty {
668 return Err(Error::BranchSuccParamTyMismatch {
672 block_param_ty: block_ssa_val.ty,
673 branch_param_ty: branch_ssa_val.ty,
682 pub fn try_get_operand(&self, inst: InstIdx, operand_idx: usize) -> Result<&Operand> {
685 .ok_or(Error::OperandIndexOutOfRange { inst, operand_idx })
689 #[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
691 pub params: Vec<SSAValIdx>,
692 pub insts: InstRange,
693 pub preds: BTreeSet<BlockIdx>,
694 pub immediate_dominator: Option<BlockIdx>,
698 fn validate(&self, block: BlockIdx, func: &FnFields, global_state: &GlobalState) -> Result<()> {
703 immediate_dominator: _, // validated by Function::new_with_global_state
705 const _: () = assert!(BlockIdx::ENTRY_BLOCK.get() == 0);
706 let expected_start = if block == BlockIdx::ENTRY_BLOCK {
709 func.blocks[block.prev()].insts.end
711 if insts.start != expected_start {
712 return Err(Error::BlockHasInvalidStart {
717 let term_inst_idx = insts.last().ok_or(Error::BlockIsEmpty { block })?;
719 .get(term_inst_idx.get())
720 .ok_or(Error::BlockEndOutOfRange { end: insts.end })?;
721 if block.get() == func.blocks.len() - 1 && insts.end.get() != func.insts.len() {
722 return Err(Error::InstHasNoBlock { inst: insts.end });
724 if block == BlockIdx::ENTRY_BLOCK {
725 if !params.is_empty() {
726 return Err(Error::EntryBlockCantHaveParams);
728 if !preds.is_empty() {
729 return Err(Error::EntryBlockCantHavePreds);
733 func.insts[inst].validate(block, inst, func, global_state)?;
735 for (param_idx, &ssa_val_idx) in params.iter().enumerate() {
736 let ssa_val = func.try_get_ssa_val(ssa_val_idx)?;
737 let def = SSAValDef::BlockParam { block, param_idx };
738 if ssa_val.def != def {
739 return Err(Error::MismatchedBlockParamDef {
747 let (term_inst, BlockTermInstKind { succs_and_params }) =
748 func.try_get_block_term_inst_and_kind(pred)?;
749 if !succs_and_params.contains_key(&block) {
750 return Err(Error::PredMissingFromPredsTermBranchsTargets {
752 branch_inst: term_inst,
756 if preds.len() > 1 && succs_and_params.len() > 1 {
757 return Err(Error::CriticalEdgeNotAllowed {
759 branch_inst: term_inst,
769 #[fields_ty = FnFields]
770 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
771 pub struct Function {
772 pub ssa_vals: Vec<SSAVal>,
773 pub insts: Vec<Inst>,
774 pub blocks: Vec<Block>,
776 /// map from blocks' start instruction's index to their block index, doesn't contain the entry block
777 pub start_inst_to_block_map: BTreeMap<InstIdx, BlockIdx>,
782 pub fn new(fields: FnFields) -> Result<Self> {
783 GlobalState::get(|global_state| Self::new_with_global_state(fields, global_state))
785 pub fn new_with_global_state(mut fields: FnFields, global_state: &GlobalState) -> Result<Self> {
786 fields.fill_start_inst_to_block_map();
791 start_inst_to_block_map: _,
794 .get(BlockIdx::ENTRY_BLOCK.get())
795 .ok_or(Error::MissingEntryBlock)?;
796 for (idx, block) in blocks.iter().enumerate() {
797 block.validate(BlockIdx::new(idx), &fields, global_state)?;
799 let dominators = dominators::simple_fast(&fields, BlockIdx::ENTRY_BLOCK);
800 for (idx, block) in blocks.iter().enumerate() {
801 let block_idx = BlockIdx::new(idx);
802 let expected = dominators.immediate_dominator(block_idx);
803 if block.immediate_dominator != expected {
804 return Err(Error::IncorrectImmediateDominator {
806 found: block.immediate_dominator,
811 for (idx, ssa_val) in ssa_vals.iter().enumerate() {
812 ssa_val.validate(SSAValIdx::new(idx), &fields)?;
816 pub fn entry_block(&self) -> &Block {
819 pub fn block_term_kind(&self, block: BlockIdx) -> &BlockTermInstKind {
820 self.insts[self.blocks[block].insts.last().unwrap()]
828 pub fn fill_start_inst_to_block_map(&mut self) {
829 self.start_inst_to_block_map.clear();
830 for (idx, block) in self.blocks.iter().enumerate() {
831 let block_idx = BlockIdx::new(idx);
832 if block_idx != BlockIdx::ENTRY_BLOCK {
833 self.start_inst_to_block_map
834 .insert(block.insts.start, block_idx);
838 pub fn try_get_ssa_val(&self, idx: SSAValIdx) -> Result<&SSAVal> {
841 .ok_or(Error::SSAValIdxOutOfRange { idx })
843 pub fn try_get_inst(&self, idx: InstIdx) -> Result<&Inst> {
846 .ok_or(Error::InstIdxOutOfRange { idx })
848 pub fn try_get_inst_mut(&mut self, idx: InstIdx) -> Result<&mut Inst> {
851 .ok_or(Error::InstIdxOutOfRange { idx })
853 pub fn try_get_operand(&self, inst: InstIdx, operand_idx: usize) -> Result<&Operand> {
854 self.try_get_inst(inst)?.try_get_operand(inst, operand_idx)
856 pub fn try_get_block(&self, idx: BlockIdx) -> Result<&Block> {
859 .ok_or(Error::BlockIdxOutOfRange { idx })
861 pub fn try_get_block_param(&self, block: BlockIdx, param_idx: usize) -> Result<SSAValIdx> {
862 self.try_get_block(block)?
866 .ok_or(Error::BlockParamIdxOutOfRange { block, param_idx })
868 pub fn try_get_block_term_inst_idx(&self, block: BlockIdx) -> Result<InstIdx> {
869 self.try_get_block(block)?
872 .ok_or(Error::BlockIsEmpty { block })
874 pub fn try_get_block_term_inst_and_kind(
877 ) -> Result<(InstIdx, &BlockTermInstKind)> {
878 let term_idx = self.try_get_block_term_inst_idx(block)?;
880 .try_get_inst(term_idx)?
883 .ok_or(Error::BlocksLastInstMustBeTerm { term_idx })?;
884 Ok((term_idx, term_kind))
886 pub fn try_get_block_term_inst_and_kind_mut(
889 ) -> Result<(InstIdx, &mut BlockTermInstKind)> {
890 let term_idx = self.try_get_block_term_inst_idx(block)?;
892 .try_get_inst_mut(term_idx)?
895 .ok_or(Error::BlocksLastInstMustBeTerm { term_idx })?;
896 Ok((term_idx, term_kind))
898 pub fn try_get_branch_target_params(
900 branch_inst: InstIdx,
902 ) -> Result<&[SSAValIdx]> {
903 let inst = self.try_get_inst(branch_inst)?;
904 let BlockTermInstKind { succs_and_params } = inst
907 .ok_or(Error::InstIsNotBlockTerm { inst: branch_inst })?;
910 .ok_or(Error::BranchTargetNotFound {
915 pub fn try_get_branch_target_param(
917 branch_inst: InstIdx,
920 ) -> Result<SSAValIdx> {
922 .try_get_branch_target_params(branch_inst, succ)?
924 .ok_or(Error::BranchTargetParamIdxOutOfRange {
930 pub fn inst_to_block(&self, inst: InstIdx) -> BlockIdx {
931 self.start_inst_to_block_map
935 .unwrap_or(BlockIdx::ENTRY_BLOCK)
939 impl Index<SSAValIdx> for Vec<SSAVal> {
940 type Output = SSAVal;
942 fn index(&self, index: SSAValIdx) -> &Self::Output {
947 impl IndexMut<SSAValIdx> for Vec<SSAVal> {
948 fn index_mut(&mut self, index: SSAValIdx) -> &mut Self::Output {
949 &mut self[index.get()]
953 impl Index<InstIdx> for Vec<Inst> {
956 fn index(&self, index: InstIdx) -> &Self::Output {
961 impl IndexMut<InstIdx> for Vec<Inst> {
962 fn index_mut(&mut self, index: InstIdx) -> &mut Self::Output {
963 &mut self[index.get()]
967 impl Index<BlockIdx> for Vec<Block> {
970 fn index(&self, index: BlockIdx) -> &Self::Output {
975 impl IndexMut<BlockIdx> for Vec<Block> {
976 fn index_mut(&mut self, index: BlockIdx) -> &mut Self::Output {
977 &mut self[index.get()]
981 impl GraphBase for FnFields {
982 type EdgeId = (BlockIdx, BlockIdx);
983 type NodeId = BlockIdx;
986 pub struct Neighbors<'a> {
987 iter: Option<btree_map::Keys<'a, BlockIdx, Vec<SSAValIdx>>>,
990 impl Iterator for Neighbors<'_> {
991 type Item = BlockIdx;
993 fn next(&mut self) -> Option<Self::Item> {
994 Some(*self.iter.as_mut()?.next()?)
998 impl<'a> IntoNeighbors for &'a FnFields {
999 type Neighbors = Neighbors<'a>;
1001 fn neighbors(self, block_idx: Self::NodeId) -> Self::Neighbors {
1004 .try_get_block_term_inst_and_kind(block_idx)
1006 .map(|(_, BlockTermInstKind { succs_and_params })| succs_and_params.keys()),
1011 pub struct VisitedMap(HashSet<BlockIdx>);
1013 impl VisitMap<BlockIdx> for VisitedMap {
1014 fn visit(&mut self, block: BlockIdx) -> bool {
1015 self.0.insert(block)
1018 fn is_visited(&self, block: &BlockIdx) -> bool {
1019 self.0.contains(block)
1023 impl Visitable for FnFields {
1024 type Map = VisitedMap;
1026 fn visit_map(&self) -> Self::Map {
1027 VisitedMap(HashSet::new())
1030 fn reset_map(&self, map: &mut Self::Map) {
1035 impl GraphProp for FnFields {
1036 type EdgeType = Directed;
1042 use crate::loc::TyFields;
1043 use std::num::NonZeroU32;
1046 fn test_constraint_non_fixed_choices_for_ty() {
1049 enum ConstraintWithoutFixedLoc {
1054 #[allow(non_snake_case)]
1060 fn add(&mut self, constraint: &Constraint) {
1062 Constraint::FixedLoc(_) => {}
1063 $(Constraint::$field => self.$field = true,)*
1067 $(assert!(self.$field, "never seen field: {}", stringify!($field));)*
1073 enum ConstraintWithoutFixedLoc {
1082 let mut seen = Seen::default();
1083 for base_ty in 0..BaseTy::LENGTH {
1084 let base_ty = BaseTy::from_usize(base_ty);
1085 for reg_len in [1, 2, 100] {
1086 let reg_len = NonZeroU32::new(reg_len).unwrap();
1087 let ty = Ty::new_or_scalar(TyFields { base_ty, reg_len });
1088 let non_fixed_choices = Constraint::non_fixed_choices_for_ty(ty);
1089 assert_eq!(non_fixed_choices.first(), Some(&Constraint::Any));
1090 assert_eq!(non_fixed_choices.last(), Some(&Constraint::Stack));
1091 for constraint in non_fixed_choices {
1092 assert_eq!(constraint.fixed_loc(), None);
1093 seen.add(constraint);
1094 if constraint.check_for_ty_mismatch(ty).is_err() {
1095 panic!("constraint ty mismatch: constraint={constraint:?} ty={ty:?}");