use crate::{
- index::{BlockIdx, DisplayOptionIdx, InstIdx, SSAValIdx},
+ index::{BlockIdx, BlockParamIdx, DisplayOptionIdx, IndexTy, InstIdx, OperandIdx, SSAValIdx},
loc::{BaseTy, Ty},
};
+use std::fmt;
use thiserror::Error;
+#[derive(Debug, Error)]
+#[error("SSA value index {idx} out of range")]
+pub struct SSAValIdxOutOfRange {
+ pub idx: SSAValIdx,
+}
+
+impl SSAValIdxOutOfRange {
+ pub fn with_inst_and_operand(
+ self,
+ inst: InstIdx,
+ operand: OperandIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ AlwaysNone,
+ OperandIdx,
+ AlwaysNone,
+ SSAValIdxOutOfRange,
+ > {
+ WithContext {
+ block: None,
+ inst,
+ succ: AlwaysNone,
+ operand,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+ pub fn with_block_inst_and_operand(
+ self,
+ block: BlockIdx,
+ inst: InstIdx,
+ operand: OperandIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ AlwaysNone,
+ OperandIdx,
+ AlwaysNone,
+ SSAValIdxOutOfRange,
+ > {
+ WithContext {
+ block: Some(block),
+ inst,
+ succ: AlwaysNone,
+ operand,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+ pub fn with_inst_succ_and_param(
+ self,
+ inst: InstIdx,
+ succ: BlockIdx,
+ param: BlockParamIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ BlockIdx,
+ AlwaysNone,
+ BlockParamIdx,
+ SSAValIdxOutOfRange,
+ > {
+ WithContext {
+ block: None,
+ inst,
+ succ,
+ operand: AlwaysNone,
+ param,
+ out_of_range_error: self,
+ }
+ }
+ pub fn with_block_inst_succ_and_param(
+ self,
+ block: BlockIdx,
+ inst: InstIdx,
+ succ: BlockIdx,
+ param: BlockParamIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ BlockIdx,
+ AlwaysNone,
+ BlockParamIdx,
+ SSAValIdxOutOfRange,
+ > {
+ WithContext {
+ block: Some(block),
+ inst,
+ succ,
+ operand: AlwaysNone,
+ param,
+ out_of_range_error: self,
+ }
+ }
+ pub fn with_block_and_param(
+ self,
+ block: BlockIdx,
+ param: BlockParamIdx,
+ ) -> WithContext<BlockIdx, AlwaysNone, AlwaysNone, AlwaysNone, BlockParamIdx, SSAValIdxOutOfRange>
+ {
+ WithContext {
+ block,
+ inst: AlwaysNone,
+ succ: AlwaysNone,
+ operand: AlwaysNone,
+ param,
+ out_of_range_error: self,
+ }
+ }
+}
+
+#[derive(Debug, Error)]
+#[error("instruction index {idx} out of range")]
+pub struct InstIdxOutOfRange {
+ pub idx: InstIdx,
+}
+
+impl InstIdxOutOfRange {
+ pub fn with_block(
+ self,
+ block: BlockIdx,
+ ) -> WithContext<BlockIdx, AlwaysNone, AlwaysNone, AlwaysNone, AlwaysNone, InstIdxOutOfRange>
+ {
+ WithContext {
+ block,
+ inst: AlwaysNone,
+ succ: AlwaysNone,
+ operand: AlwaysNone,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+}
+
+#[derive(Debug, Error)]
+#[error("block index {idx} out of range")]
+pub struct BlockIdxOutOfRange {
+ pub idx: BlockIdx,
+}
+
+#[derive(Debug, Error)]
+#[error("block parameter index {idx} is out of range")]
+pub struct BlockParamIdxOutOfRange {
+ pub idx: BlockParamIdx,
+}
+
+impl BlockParamIdxOutOfRange {
+ pub fn with_block(
+ self,
+ block: BlockIdx,
+ ) -> WithContext<
+ BlockIdx,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ BlockParamIdxOutOfRange,
+ > {
+ WithContext {
+ block,
+ inst: AlwaysNone,
+ succ: AlwaysNone,
+ operand: AlwaysNone,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+ pub fn with_block_inst_and_succ(
+ self,
+ block: BlockIdx,
+ inst: InstIdx,
+ succ: BlockIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ BlockIdx,
+ AlwaysNone,
+ AlwaysNone,
+ BlockParamIdxOutOfRange,
+ > {
+ WithContext {
+ block: Some(block),
+ inst,
+ succ,
+ operand: AlwaysNone,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+ pub fn with_inst_and_succ(
+ self,
+ inst: InstIdx,
+ succ: BlockIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ BlockIdx,
+ AlwaysNone,
+ AlwaysNone,
+ BlockParamIdxOutOfRange,
+ > {
+ WithContext {
+ block: None,
+ inst,
+ succ,
+ operand: AlwaysNone,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+}
+
+#[derive(Debug, Error)]
+#[error("operand index {idx} is out of range")]
+pub struct OperandIdxOutOfRange {
+ pub idx: OperandIdx,
+}
+
+impl OperandIdxOutOfRange {
+ pub fn with_block_and_inst(
+ self,
+ block: BlockIdx,
+ inst: InstIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ OperandIdxOutOfRange,
+ > {
+ WithContext {
+ block: Some(block),
+ inst,
+ succ: AlwaysNone,
+ operand: AlwaysNone,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+ pub fn with_inst(
+ self,
+ inst: InstIdx,
+ ) -> WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ OperandIdxOutOfRange,
+ > {
+ WithContext {
+ block: None,
+ inst,
+ succ: AlwaysNone,
+ operand: AlwaysNone,
+ param: AlwaysNone,
+ out_of_range_error: self,
+ }
+ }
+}
+
+#[derive(Default, Clone, Copy, Debug)]
+pub struct AlwaysNone;
+
+pub trait ToContextValue<T: IndexTy>: fmt::Debug + Copy {
+ fn to_context_value(self) -> Option<T>;
+}
+
+impl<T: IndexTy> ToContextValue<T> for AlwaysNone {
+ fn to_context_value(self) -> Option<T> {
+ None
+ }
+}
+
+impl<T: IndexTy> ToContextValue<T> for T {
+ fn to_context_value(self) -> Option<T> {
+ Some(self)
+ }
+}
+
+impl<T: IndexTy> ToContextValue<T> for Option<T> {
+ fn to_context_value(self) -> Option<T> {
+ self
+ }
+}
+
+#[derive(Debug, Error)]
+pub struct WithContext<Block, Inst, Succ, Operand, BlockParam, E>
+where
+ Block: ToContextValue<BlockIdx>,
+ Inst: ToContextValue<InstIdx>,
+ Succ: ToContextValue<BlockIdx>,
+ Operand: ToContextValue<OperandIdx>,
+ BlockParam: ToContextValue<BlockParamIdx>,
+{
+ pub block: Block,
+ pub inst: Inst,
+ pub succ: Succ,
+ pub operand: Operand,
+ pub param: BlockParam,
+ #[source]
+ pub out_of_range_error: E,
+}
+
+impl<Block, Inst, Succ, Operand, BlockParam, E> fmt::Display
+ for WithContext<Block, Inst, Succ, Operand, BlockParam, E>
+where
+ Block: ToContextValue<BlockIdx>,
+ Inst: ToContextValue<InstIdx>,
+ Succ: ToContextValue<BlockIdx>,
+ Operand: ToContextValue<OperandIdx>,
+ BlockParam: ToContextValue<BlockParamIdx>,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let Self {
+ block,
+ inst,
+ succ,
+ operand,
+ param,
+ out_of_range_error: _,
+ } = self;
+ let block = block.to_context_value();
+ let inst = inst.to_context_value();
+ let succ = succ.to_context_value();
+ let operand = operand.to_context_value();
+ let param = param.to_context_value();
+ macro_rules! write_if {
+ ($field:ident, $f:ident, $($rest:tt)+) => {
+ if let Some($field) = $field {
+ write!($f, $($rest)+)?;
+ }
+ };
+ }
+ if block.is_some() || inst.is_some() {
+ write!(f, " in ")?;
+ }
+ write_if!(block, f, "{block}");
+ if block.is_some() && inst.is_some() {
+ write!(f, ":")?;
+ }
+ write_if!(inst, f, "{inst}");
+ write_if!(succ, f, " in successor {succ}");
+ write_if!(operand, f, " in {operand}");
+ write_if!(param, f, " in block parameter {param}");
+ Ok(())
+ }
+}
+
#[derive(Debug, Error)]
pub enum Error {
+ #[error(transparent)]
+ SSAValIdxOutOfRangeWithBlockInstAndOperand(
+ #[from]
+ WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ AlwaysNone,
+ OperandIdx,
+ AlwaysNone,
+ SSAValIdxOutOfRange,
+ >,
+ ),
+ #[error(transparent)]
+ SSAValIdxOutOfRangeWithBlockInstSuccAndParam(
+ #[from]
+ WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ BlockIdx,
+ AlwaysNone,
+ BlockParamIdx,
+ SSAValIdxOutOfRange,
+ >,
+ ),
+ #[error(transparent)]
+ SSAValIdxOutOfRangeWithBlockAndParam(
+ #[from]
+ WithContext<
+ BlockIdx,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ BlockParamIdx,
+ SSAValIdxOutOfRange,
+ >,
+ ),
+ #[error(transparent)]
+ InstIdxOutOfRangeWithBlock(
+ #[from]
+ WithContext<BlockIdx, AlwaysNone, AlwaysNone, AlwaysNone, AlwaysNone, InstIdxOutOfRange>,
+ ),
+ #[error(transparent)]
+ InstIdxOutOfRange(#[from] InstIdxOutOfRange),
+ #[error(transparent)]
+ BlockIdxOutOfRange(#[from] BlockIdxOutOfRange),
+ #[error(transparent)]
+ BlockParamIdxOutOfRangeWithBlock(
+ #[from]
+ WithContext<
+ BlockIdx,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ BlockParamIdxOutOfRange,
+ >,
+ ),
+ #[error(transparent)]
+ BlockParamIdxOutOfRangeWithBlockInstAndSucc(
+ #[from]
+ WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ BlockIdx,
+ AlwaysNone,
+ AlwaysNone,
+ BlockParamIdxOutOfRange,
+ >,
+ ),
+ #[error(transparent)]
+ OperandIdxOutOfRangeWithBlockAndInst(
+ #[from]
+ WithContext<
+ Option<BlockIdx>,
+ InstIdx,
+ AlwaysNone,
+ AlwaysNone,
+ AlwaysNone,
+ OperandIdxOutOfRange,
+ >,
+ ),
#[error("can't create a vector of an only-scalar type: {base_ty:?}")]
TriedToCreateVectorOfOnlyScalarType { base_ty: BaseTy },
#[error("reg_len out of range")]
TermInstOnlyAllowedAtBlockEnd { inst_idx: InstIdx },
#[error("instruction not in a block: {inst}")]
InstHasNoBlock { inst: InstIdx },
- #[error("operand index {operand_idx} out of range for {inst}")]
- OperandIndexOutOfRange { inst: InstIdx, operand_idx: usize },
#[error("duplicate copy destination operand: operand index {operand_idx} for {inst}")]
- DupCopyDestOperand { inst: InstIdx, operand_idx: usize },
- #[error("SSA value index {idx} out of range")]
- SSAValIdxOutOfRange { idx: SSAValIdx },
- #[error("instruction index {idx} out of range")]
- InstIdxOutOfRange { idx: InstIdx },
- #[error("block index {idx} out of range")]
- BlockIdxOutOfRange { idx: BlockIdx },
+ DupCopyDestOperand {
+ inst: InstIdx,
+ operand_idx: OperandIdx,
+ },
#[error("copy instruction's source type doesn't match source operands")]
CopySrcTyMismatch { inst: InstIdx },
#[error("copy instruction's destination type doesn't match destination operands")]
MissingOperandUse {
ssa_val_idx: SSAValIdx,
inst: InstIdx,
- operand_idx: usize,
+ operand_idx: OperandIdx,
},
#[error(
"operand index {operand_idx} for {inst} has kind `Def` but isn't \
OperandDefIsNotSSAValDef {
ssa_val_idx: SSAValIdx,
inst: InstIdx,
- operand_idx: usize,
+ operand_idx: OperandIdx,
},
#[error(
"SSA value {ssa_val_idx}'s definition isn't the corresponding \
SSAValDefIsNotOperandsSSAVal {
ssa_val_idx: SSAValIdx,
inst: InstIdx,
- operand_idx: usize,
+ operand_idx: OperandIdx,
},
#[error(
"SSA value {ssa_val_idx}'s type can't be used with the constraint on \
ConstraintTyMismatch {
ssa_val_idx: SSAValIdx,
inst: InstIdx,
- operand_idx: usize,
+ operand_idx: OperandIdx,
},
#[error(
"fixed location constraint on operand index {operand_idx} for \
{inst} conflicts with clobbers"
)]
- FixedLocConflictsWithClobbers { inst: InstIdx, operand_idx: usize },
+ FixedLocConflictsWithClobbers {
+ inst: InstIdx,
+ operand_idx: OperandIdx,
+ },
#[error("operand kind must be def")]
OperandKindMustBeDef,
#[error(
)]
ReuseTargetOperandMustBeUse {
inst: InstIdx,
- reuse_target_operand_idx: usize,
+ reuse_target_operand_idx: OperandIdx,
},
#[error(
"source block {src_block} missing from branch {branch_inst}'s \
ssa_val_idx: SSAValIdx,
inst: InstIdx,
succ: BlockIdx,
- param_idx: usize,
+ param_idx: BlockParamIdx,
},
#[error(
"the number of parameters ({branch_param_count}) for branch {inst}'s \
BranchSuccParamTyMismatch {
inst: InstIdx,
succ: BlockIdx,
- param_idx: usize,
+ param_idx: BlockParamIdx,
block_param_ty: Ty,
branch_param_ty: Ty,
},
MismatchedBlockParamDef {
ssa_val_idx: SSAValIdx,
block: BlockIdx,
- param_idx: usize,
+ param_idx: BlockParamIdx,
},
#[error(
"predecessor {src_block} of target block {tgt_block} is missing from \
branch_inst: InstIdx,
tgt_block: BlockIdx,
},
- #[error("block parameter index {param_idx} is out of range for block {block}")]
- BlockParamIdxOutOfRange { block: BlockIdx, param_idx: usize },
#[error(
"SSA value {ssa_val_idx}'s use isn't the corresponding \
operand's SSA Value: operand index {operand_idx} for {inst}"
SSAValUseIsNotOperandsSSAVal {
ssa_val_idx: SSAValIdx,
inst: InstIdx,
- operand_idx: usize,
+ operand_idx: OperandIdx,
},
#[error(
"SSA value {ssa_val_idx} is use as a branch instruction {inst}'s \
branch_inst: InstIdx,
tgt_block: BlockIdx,
},
- #[error(
- "branch instruction {branch_inst}'s block parameter index {param_idx} \
- is out of range for target block {tgt_block}"
- )]
- BranchTargetParamIdxOutOfRange {
- branch_inst: InstIdx,
- tgt_block: BlockIdx,
- param_idx: usize,
- },
#[error(
"SSA value {ssa_val_idx}'s use isn't the corresponding \
branch {branch_inst}'s target block parameter's SSA Value for \
ssa_val_idx: SSAValIdx,
branch_inst: InstIdx,
tgt_block: BlockIdx,
- param_idx: usize,
+ param_idx: BlockParamIdx,
},
#[error(
"block {block_idx} has incorrect immediate dominator: expected \
)]
ReuseOperandTyMismatch {
inst: InstIdx,
- tgt_operand_idx: usize,
- src_operand_idx: usize,
+ tgt_operand_idx: OperandIdx,
+ src_operand_idx: OperandIdx,
src_ty: Ty,
tgt_ty: Ty,
},
use crate::{
- error::{Error, Result},
- index::{BlockIdx, InstIdx, InstRange, SSAValIdx},
+ error::{
+ BlockIdxOutOfRange, BlockParamIdxOutOfRange, Error, InstIdxOutOfRange,
+ OperandIdxOutOfRange, Result, SSAValIdxOutOfRange,
+ },
+ index::{
+ BlockIdx, BlockParamIdx, IndexTy, InstIdx, InstRange, OperandIdx, RangeIter, SSAValIdx,
+ },
interned::{GlobalState, Intern, Interned},
loc::{BaseTy, Loc, Ty},
loc_set::LocSet,
use smallvec::SmallVec;
use std::{
collections::{btree_map, BTreeMap, BTreeSet},
+ iter::FusedIterator,
mem,
ops::{Index, IndexMut},
};
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub enum SSAValDef {
- BlockParam { block: BlockIdx, param_idx: usize },
- Operand { inst: InstIdx, operand_idx: usize },
+ BlockParam {
+ block: BlockIdx,
+ param_idx: BlockParamIdx,
+ },
+ Operand {
+ inst: InstIdx,
+ operand_idx: OperandIdx,
+ },
+}
+
+impl SSAValDef {
+ pub const fn invalid() -> Self {
+ SSAValDef::BlockParam {
+ block: BlockIdx::ENTRY_BLOCK,
+ param_idx: BlockParamIdx::new(!0),
+ }
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
pub struct BranchSuccParamUse {
pub branch_inst: InstIdx,
pub succ: BlockIdx,
- pub param_idx: usize,
+ pub param_idx: BlockParamIdx,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
pub struct OperandUse {
pub inst: InstIdx,
- pub operand_idx: usize,
+ pub operand_idx: OperandIdx,
}
#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub struct SSAVal {
pub ty: Ty,
+ #[serde(skip, default = "SSAValDef::invalid")]
pub def: SSAValDef,
+ #[serde(skip)]
pub operand_uses: BTreeSet<OperandUse>,
+ #[serde(skip)]
pub branch_succ_param_uses: BTreeSet<BranchSuccParamUse>,
}
pub enum KindAndConstraint {
Reuse {
kind: OperandKindDefOnly,
- reuse_operand_idx: usize,
+ reuse_operand_idx: OperandIdx,
},
Constraint {
kind: OperandKind,
}
fn validate(
self,
- _block: BlockIdx,
+ block: BlockIdx,
inst: InstIdx,
- operand_idx: usize,
+ operand_idx: OperandIdx,
func: &FnFields,
global_state: &GlobalState,
) -> Result<()> {
kind_and_constraint,
stage: _,
} = self;
- let ssa_val = func.try_get_ssa_val(ssa_val_idx)?;
+ let ssa_val = func
+ .ssa_vals
+ .try_index(ssa_val_idx)
+ .map_err(|e| e.with_block_inst_and_operand(block, inst, operand_idx))?;
match kind_and_constraint.kind() {
OperandKind::Use => {
if !ssa_val
} = self.kind_and_constraint
{
let reuse_src = func.try_get_operand(inst, reuse_operand_idx)?;
- let reuse_src_ssa_val = func.try_get_ssa_val(reuse_src.ssa_val)?;
+ let reuse_src_ssa_val = func
+ .ssa_vals
+ .try_index(reuse_src.ssa_val)
+ .map_err(|e| e.with_block_inst_and_operand(block, inst, reuse_operand_idx))?;
if ssa_val.ty != reuse_src_ssa_val.ty {
return Err(Error::ReuseOperandTyMismatch {
inst,
})?;
if let Some(fixed_loc) = constraint.fixed_loc() {
if func
- .try_get_inst(inst)?
+ .insts
+ .try_index(inst)?
.clobbers
.clone()
.conflicts_with(fixed_loc, global_state)
/// copy concatenates all `srcs` together and de-concatenates the result into all `dests`.
#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub struct CopyInstKind {
- pub src_operand_idxs: Vec<usize>,
- pub dest_operand_idxs: Vec<usize>,
+ pub src_operand_idxs: Vec<OperandIdx>,
+ pub dest_operand_idxs: Vec<OperandIdx>,
pub copy_ty: Ty,
}
impl CopyInstKind {
- fn calc_copy_ty(operand_idxs: &[usize], inst: InstIdx, func: &FnFields) -> Result<Option<Ty>> {
+ fn calc_copy_ty(
+ operand_idxs: &[OperandIdx],
+ inst: InstIdx,
+ func: &FnFields,
+ ) -> Result<Option<Ty>> {
let mut retval: Option<Ty> = None;
for &operand_idx in operand_idxs {
let operand = func.try_get_operand(inst, operand_idx)?;
- let ssa_val = func.try_get_ssa_val(operand.ssa_val)?;
+ let ssa_val = func
+ .ssa_vals
+ .try_index(operand.ssa_val)
+ .map_err(|e| e.with_inst_and_operand(inst, operand_idx))?;
retval = Some(match retval {
Some(retval) => retval.try_concat(ssa_val.ty)?,
None => ssa_val.ty,
Error::TermInstOnlyAllowedAtBlockEnd { inst_idx: inst }
});
}
- for (idx, operand) in operands.iter().enumerate() {
- operand.validate(block, inst, idx, func, global_state)?;
+ for (operand_idx, operand) in operands.entries() {
+ operand.validate(block, inst, operand_idx, func, global_state)?;
}
match kind {
InstKind::Normal => {}
let mut seen_dest_operands = SmallVec::<[bool; 16]>::new();
seen_dest_operands.resize(operands.len(), false);
for &dest_operand_idx in dest_operand_idxs {
- let seen_dest_operand = seen_dest_operands.get_mut(dest_operand_idx).ok_or(
- Error::OperandIndexOutOfRange {
- inst,
- operand_idx: dest_operand_idx,
- },
- )?;
+ let seen_dest_operand = seen_dest_operands
+ .get_mut(dest_operand_idx.get())
+ .ok_or_else(|| {
+ OperandIdxOutOfRange {
+ idx: dest_operand_idx,
+ }
+ .with_inst(inst)
+ })?;
if mem::replace(seen_dest_operand, true) {
return Err(Error::DupCopyDestOperand {
inst,
}
InstKind::BlockTerm(BlockTermInstKind { succs_and_params }) => {
for (&succ_idx, params) in succs_and_params {
- let succ = func.try_get_block(succ_idx)?;
+ let succ = func.blocks.try_index(succ_idx)?;
if !succ.preds.contains(&block) {
return Err(Error::SrcBlockMissingFromBranchTgtBlocksPreds {
src_block: block,
branch_param_count: params.len(),
});
}
- for (param_idx, (&branch_ssa_val_idx, &block_ssa_val_idx)) in
- params.iter().zip(&succ.params).enumerate()
+ for ((param_idx, &branch_ssa_val_idx), &block_ssa_val_idx) in
+ params.entries().zip(&succ.params)
{
- let branch_ssa_val = func.try_get_ssa_val(branch_ssa_val_idx)?;
- let block_ssa_val = func.try_get_ssa_val(block_ssa_val_idx)?;
+ let branch_ssa_val = func
+ .ssa_vals
+ .try_index(branch_ssa_val_idx)
+ .map_err(|e| e.with_inst_succ_and_param(inst, succ_idx, param_idx))?;
+ let block_ssa_val = func
+ .ssa_vals
+ .try_index(block_ssa_val_idx)
+ .map_err(|e| e.with_block_and_param(succ_idx, param_idx))?;
if !branch_ssa_val
.branch_succ_param_uses
.contains(&BranchSuccParamUse {
}
Ok(())
}
- pub fn try_get_operand(&self, inst: InstIdx, operand_idx: usize) -> Result<&Operand> {
+ pub fn try_get_operand(&self, inst: InstIdx, operand_idx: OperandIdx) -> Result<&Operand> {
self.operands
- .get(operand_idx)
- .ok_or(Error::OperandIndexOutOfRange { inst, operand_idx })
+ .try_index(operand_idx)
+ .map_err(|e| e.with_inst(inst).into())
}
}
for inst in *insts {
func.insts[inst].validate(block, inst, func, global_state)?;
}
- for (param_idx, &ssa_val_idx) in params.iter().enumerate() {
- let ssa_val = func.try_get_ssa_val(ssa_val_idx)?;
+ for (param_idx, &ssa_val_idx) in params.entries() {
+ let ssa_val = func
+ .ssa_vals
+ .try_index(ssa_val_idx)
+ .map_err(|e| e.with_block_and_param(block, param_idx))?;
let def = SSAValDef::BlockParam { block, param_idx };
if ssa_val.def != def {
return Err(Error::MismatchedBlockParamDef {
}
pub fn new_with_global_state(mut fields: FnFields, global_state: &GlobalState) -> Result<Self> {
fields.fill_start_inst_to_block_map();
+ fields.fill_ssa_defs_uses()?;
let FnFields {
ssa_vals,
insts: _,
blocks
.get(BlockIdx::ENTRY_BLOCK.get())
.ok_or(Error::MissingEntryBlock)?;
- for (idx, block) in blocks.iter().enumerate() {
- block.validate(BlockIdx::new(idx), &fields, global_state)?;
+ for (block_idx, block) in blocks.entries() {
+ block.validate(block_idx, &fields, global_state)?;
}
let dominators = dominators::simple_fast(&fields, BlockIdx::ENTRY_BLOCK);
- for (idx, block) in blocks.iter().enumerate() {
- let block_idx = BlockIdx::new(idx);
+ for (block_idx, block) in blocks.entries() {
let expected = dominators.immediate_dominator(block_idx);
if block.immediate_dominator != expected {
return Err(Error::IncorrectImmediateDominator {
});
}
}
- for (idx, ssa_val) in ssa_vals.iter().enumerate() {
- ssa_val.validate(SSAValIdx::new(idx), &fields)?;
+ for (ssa_val_idx, ssa_val) in ssa_vals.entries() {
+ ssa_val.validate(ssa_val_idx, &fields)?;
}
Ok(Self(fields))
}
impl FnFields {
pub fn fill_start_inst_to_block_map(&mut self) {
self.start_inst_to_block_map.clear();
- for (idx, block) in self.blocks.iter().enumerate() {
- let block_idx = BlockIdx::new(idx);
+ for (block_idx, block) in self.blocks.entries() {
if block_idx != BlockIdx::ENTRY_BLOCK {
self.start_inst_to_block_map
.insert(block.insts.start, block_idx);
}
}
}
- pub fn try_get_ssa_val(&self, idx: SSAValIdx) -> Result<&SSAVal> {
- self.ssa_vals
- .get(idx.get())
- .ok_or(Error::SSAValIdxOutOfRange { idx })
- }
- pub fn try_get_inst(&self, idx: InstIdx) -> Result<&Inst> {
- self.insts
- .get(idx.get())
- .ok_or(Error::InstIdxOutOfRange { idx })
- }
- pub fn try_get_inst_mut(&mut self, idx: InstIdx) -> Result<&mut Inst> {
- self.insts
- .get_mut(idx.get())
- .ok_or(Error::InstIdxOutOfRange { idx })
- }
- pub fn try_get_operand(&self, inst: InstIdx, operand_idx: usize) -> Result<&Operand> {
- self.try_get_inst(inst)?.try_get_operand(inst, operand_idx)
+ pub fn fill_ssa_defs_uses(&mut self) -> Result<()> {
+ for ssa_val in &mut self.ssa_vals {
+ ssa_val.branch_succ_param_uses.clear();
+ ssa_val.operand_uses.clear();
+ ssa_val.def = SSAValDef::invalid();
+ }
+ for (block_idx, block) in self.blocks.entries() {
+ for (param_idx, ¶m) in block.params.entries() {
+ self.ssa_vals
+ .try_index_mut(param)
+ .map_err(|e| e.with_block_and_param(block_idx, param_idx))?
+ .def = SSAValDef::BlockParam {
+ block: block_idx,
+ param_idx,
+ };
+ }
+ }
+ for (inst_idx, inst) in self.insts.entries() {
+ for (operand_idx, operand) in inst.operands.entries() {
+ let ssa_val = self
+ .ssa_vals
+ .try_index_mut(operand.ssa_val)
+ .map_err(|e| e.with_inst_and_operand(inst_idx, operand_idx))?;
+ match operand.kind_and_constraint.kind() {
+ OperandKind::Use => {
+ ssa_val.operand_uses.insert(OperandUse {
+ inst: inst_idx,
+ operand_idx,
+ });
+ }
+ OperandKind::Def => {
+ ssa_val.def = SSAValDef::Operand {
+ inst: inst_idx,
+ operand_idx,
+ };
+ }
+ }
+ }
+ match &inst.kind {
+ InstKind::Normal | InstKind::Copy(_) => {}
+ InstKind::BlockTerm(BlockTermInstKind { succs_and_params }) => {
+ for (&succ, params) in succs_and_params {
+ for (param_idx, ¶m) in params.entries() {
+ let ssa_val = self.ssa_vals.try_index_mut(param).map_err(|e| {
+ e.with_inst_succ_and_param(inst_idx, succ, param_idx)
+ })?;
+ ssa_val.branch_succ_param_uses.insert(BranchSuccParamUse {
+ branch_inst: inst_idx,
+ succ,
+ param_idx,
+ });
+ }
+ }
+ }
+ }
+ }
+ Ok(())
}
- pub fn try_get_block(&self, idx: BlockIdx) -> Result<&Block> {
- self.blocks
- .get(idx.get())
- .ok_or(Error::BlockIdxOutOfRange { idx })
+ pub fn try_get_operand(&self, inst: InstIdx, operand_idx: OperandIdx) -> Result<&Operand> {
+ Ok(self
+ .insts
+ .try_index(inst)?
+ .operands
+ .try_index(operand_idx)
+ .map_err(|e| e.with_inst(inst))?)
}
- pub fn try_get_block_param(&self, block: BlockIdx, param_idx: usize) -> Result<SSAValIdx> {
- self.try_get_block(block)?
+ pub fn try_get_block_param(
+ &self,
+ block: BlockIdx,
+ param_idx: BlockParamIdx,
+ ) -> Result<SSAValIdx> {
+ Ok(*self
+ .blocks
+ .try_index(block)?
.params
- .get(param_idx)
- .copied()
- .ok_or(Error::BlockParamIdxOutOfRange { block, param_idx })
+ .try_index(param_idx)
+ .map_err(|e| e.with_block(block))?)
}
pub fn try_get_block_term_inst_idx(&self, block: BlockIdx) -> Result<InstIdx> {
- self.try_get_block(block)?
+ self.blocks
+ .try_index(block)?
.insts
.last()
.ok_or(Error::BlockIsEmpty { block })
) -> Result<(InstIdx, &BlockTermInstKind)> {
let term_idx = self.try_get_block_term_inst_idx(block)?;
let term_kind = self
- .try_get_inst(term_idx)?
+ .insts
+ .try_index(term_idx)?
.kind
.block_term()
.ok_or(Error::BlocksLastInstMustBeTerm { term_idx })?;
) -> Result<(InstIdx, &mut BlockTermInstKind)> {
let term_idx = self.try_get_block_term_inst_idx(block)?;
let term_kind = self
- .try_get_inst_mut(term_idx)?
+ .insts
+ .try_index_mut(term_idx)?
.kind
.block_term_mut()
.ok_or(Error::BlocksLastInstMustBeTerm { term_idx })?;
branch_inst: InstIdx,
succ: BlockIdx,
) -> Result<&[SSAValIdx]> {
- let inst = self.try_get_inst(branch_inst)?;
+ let inst = self.insts.try_index(branch_inst)?;
let BlockTermInstKind { succs_and_params } = inst
.kind
.block_term()
&self,
branch_inst: InstIdx,
succ: BlockIdx,
- param_idx: usize,
+ param_idx: BlockParamIdx,
) -> Result<SSAValIdx> {
Ok(*self
.try_get_branch_target_params(branch_inst, succ)?
- .get(param_idx)
- .ok_or(Error::BranchTargetParamIdxOutOfRange {
- branch_inst,
- tgt_block: succ,
- param_idx,
- })?)
+ .try_index(param_idx)
+ .map_err(|e: BlockParamIdxOutOfRange| e.with_inst_and_succ(branch_inst, succ))?)
}
pub fn inst_to_block(&self, inst: InstIdx) -> BlockIdx {
self.start_inst_to_block_map
}
}
-impl Index<SSAValIdx> for Vec<SSAVal> {
- type Output = SSAVal;
+pub trait Entries<'a, I>: Index<I>
+where
+ I: IndexTy,
+ Self::Output: 'a,
+{
+ type Iter: Iterator<Item = (I, &'a Self::Output)>
+ + DoubleEndedIterator
+ + ExactSizeIterator
+ + FusedIterator;
+ fn entries(&'a self) -> Self::Iter;
+ fn keys(&'a self) -> RangeIter<I>;
+}
- fn index(&self, index: SSAValIdx) -> &Self::Output {
- &self[index.get()]
- }
+pub trait EntriesMut<'a, I>: Entries<'a, I> + IndexMut<I>
+where
+ I: IndexTy,
+ Self::Output: 'a,
+{
+ type IterMut: Iterator<Item = (I, &'a mut Self::Output)>
+ + DoubleEndedIterator
+ + ExactSizeIterator
+ + FusedIterator;
+ fn entries_mut(&'a mut self) -> Self::IterMut;
}
-impl IndexMut<SSAValIdx> for Vec<SSAVal> {
- fn index_mut(&mut self, index: SSAValIdx) -> &mut Self::Output {
- &mut self[index.get()]
- }
+pub trait TryIndex<I>: for<'a> Entries<'a, I>
+where
+ I: IndexTy,
+{
+ type Error;
+ fn try_index(&self, idx: I) -> Result<&Self::Output, Self::Error>;
}
-impl Index<InstIdx> for Vec<Inst> {
- type Output = Inst;
+pub trait TryIndexMut<I>: TryIndex<I> + for<'a> EntriesMut<'a, I>
+where
+ I: IndexTy,
+{
+ fn try_index_mut(&mut self, idx: I) -> Result<&mut Self::Output, Self::Error>;
+}
- fn index(&self, index: InstIdx) -> &Self::Output {
- &self[index.get()]
- }
+macro_rules! impl_index {
+ (
+ #[error = $Error:ident, iter = $Iter:ident, iter_mut = $IterMut:ident]
+ impl Index<$I:ty> for Vec<$T:ty> {}
+ ) => {
+ #[derive(Clone, Debug)]
+ pub struct $Iter<'a> {
+ iter: std::iter::Enumerate<std::slice::Iter<'a, $T>>,
+ }
+
+ impl<'a> Iterator for $Iter<'a> {
+ type Item = ($I, &'a $T);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next().map(|(i, v)| (<$I>::new(i), v))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+
+ fn fold<B, F>(self, init: B, mut f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ self.iter
+ .fold(init, move |a, (i, v)| f(a, (<$I>::new(i), v)))
+ }
+ }
+
+ impl DoubleEndedIterator for $Iter<'_> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.iter.next_back().map(|(i, v)| (<$I>::new(i), v))
+ }
+
+ fn rfold<B, F>(self, init: B, mut f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ self.iter
+ .rfold(init, move |a, (i, v)| f(a, (<$I>::new(i), v)))
+ }
+ }
+
+ impl ExactSizeIterator for $Iter<'_> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ }
+
+ impl FusedIterator for $Iter<'_> {}
+
+ #[derive(Debug)]
+ pub struct $IterMut<'a> {
+ iter: std::iter::Enumerate<std::slice::IterMut<'a, $T>>,
+ }
+
+ impl<'a> Iterator for $IterMut<'a> {
+ type Item = ($I, &'a mut $T);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next().map(|(i, v)| (<$I>::new(i), v))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+
+ fn fold<B, F>(self, init: B, mut f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ self.iter
+ .fold(init, move |a, (i, v)| f(a, (<$I>::new(i), v)))
+ }
+ }
+
+ impl DoubleEndedIterator for $IterMut<'_> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.iter.next_back().map(|(i, v)| (<$I>::new(i), v))
+ }
+
+ fn rfold<B, F>(self, init: B, mut f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ self.iter
+ .rfold(init, move |a, (i, v)| f(a, (<$I>::new(i), v)))
+ }
+ }
+
+ impl ExactSizeIterator for $IterMut<'_> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ }
+
+ impl FusedIterator for $IterMut<'_> {}
+
+ impl Index<$I> for Vec<$T> {
+ type Output = $T;
+
+ fn index(&self, index: $I) -> &Self::Output {
+ &self[index.get()]
+ }
+ }
+
+ impl IndexMut<$I> for Vec<$T> {
+ fn index_mut(&mut self, index: $I) -> &mut Self::Output {
+ &mut self[index.get()]
+ }
+ }
+
+ impl<'a> Entries<'a, $I> for Vec<$T> {
+ type Iter = $Iter<'a>;
+ fn entries(&'a self) -> Self::Iter {
+ $Iter {
+ iter: (**self).iter().enumerate(),
+ }
+ }
+ fn keys(&'a self) -> RangeIter<$I> {
+ RangeIter::from_usize_range(0..self.len())
+ }
+ }
+
+ impl<'a> EntriesMut<'a, $I> for Vec<$T> {
+ type IterMut = $IterMut<'a>;
+ fn entries_mut(&'a mut self) -> Self::IterMut {
+ $IterMut {
+ iter: (**self).iter_mut().enumerate(),
+ }
+ }
+ }
+
+ impl TryIndex<$I> for Vec<$T> {
+ type Error = $Error;
+
+ fn try_index(&self, idx: $I) -> Result<&Self::Output, Self::Error> {
+ self.get(idx.get()).ok_or($Error { idx })
+ }
+ }
+
+ impl TryIndexMut<$I> for Vec<$T> {
+ fn try_index_mut(&mut self, idx: $I) -> Result<&mut Self::Output, Self::Error> {
+ self.get_mut(idx.get()).ok_or($Error { idx })
+ }
+ }
+
+ impl Index<$I> for [$T] {
+ type Output = $T;
+
+ fn index(&self, index: $I) -> &Self::Output {
+ &self[index.get()]
+ }
+ }
+
+ impl IndexMut<$I> for [$T] {
+ fn index_mut(&mut self, index: $I) -> &mut Self::Output {
+ &mut self[index.get()]
+ }
+ }
+
+ impl<'a> Entries<'a, $I> for [$T] {
+ type Iter = $Iter<'a>;
+ fn entries(&'a self) -> Self::Iter {
+ $Iter {
+ iter: self.iter().enumerate(),
+ }
+ }
+ fn keys(&'a self) -> RangeIter<$I> {
+ RangeIter::from_usize_range(0..self.len())
+ }
+ }
+
+ impl<'a> EntriesMut<'a, $I> for [$T] {
+ type IterMut = $IterMut<'a>;
+ fn entries_mut(&'a mut self) -> Self::IterMut {
+ $IterMut {
+ iter: self.iter_mut().enumerate(),
+ }
+ }
+ }
+
+ impl TryIndex<$I> for [$T] {
+ type Error = $Error;
+
+ fn try_index(&self, idx: $I) -> Result<&Self::Output, Self::Error> {
+ self.get(idx.get()).ok_or($Error { idx })
+ }
+ }
+
+ impl TryIndexMut<$I> for [$T] {
+ fn try_index_mut(&mut self, idx: $I) -> Result<&mut Self::Output, Self::Error> {
+ self.get_mut(idx.get()).ok_or($Error { idx })
+ }
+ }
+ };
}
-impl IndexMut<InstIdx> for Vec<Inst> {
- fn index_mut(&mut self, index: InstIdx) -> &mut Self::Output {
- &mut self[index.get()]
- }
+impl_index! {
+ #[error = SSAValIdxOutOfRange, iter = SSAValEntriesIter, iter_mut = SSAValEntriesIterMut]
+ impl Index<SSAValIdx> for Vec<SSAVal> {}
}
-impl Index<BlockIdx> for Vec<Block> {
- type Output = Block;
+impl_index! {
+ #[error = BlockParamIdxOutOfRange, iter = BlockParamEntriesIter, iter_mut = BlockParamEntriesIterMut]
+ impl Index<BlockParamIdx> for Vec<SSAValIdx> {}
+}
- fn index(&self, index: BlockIdx) -> &Self::Output {
- &self[index.get()]
- }
+impl_index! {
+ #[error = OperandIdxOutOfRange, iter = OperandEntriesIter, iter_mut = OperandEntriesIterMut]
+ impl Index<OperandIdx> for Vec<Operand> {}
}
-impl IndexMut<BlockIdx> for Vec<Block> {
- fn index_mut(&mut self, index: BlockIdx) -> &mut Self::Output {
- &mut self[index.get()]
- }
+impl_index! {
+ #[error = InstIdxOutOfRange, iter = InstEntriesIter, iter_mut = InstEntriesIterMut]
+ impl Index<InstIdx> for Vec<Inst> {}
+}
+
+impl_index! {
+ #[error = BlockIdxOutOfRange, iter = BlockEntriesIter, iter_mut = BlockEntriesIterMut]
+ impl Index<BlockIdx> for Vec<Block> {}
}
impl GraphBase for FnFields {
#![cfg(feature = "fuzzing")]
use crate::{
function::{
- Block, BlockTermInstKind, BranchSuccParamUse, Constraint, CopyInstKind, FnFields, Inst,
- InstKind, InstStage, KindAndConstraint, Operand, OperandKind, OperandKindDefOnly,
- OperandUse, SSAVal, SSAValDef,
+ Block, BlockTermInstKind, Constraint, CopyInstKind, Entries, EntriesMut, FnFields, Inst,
+ InstKind, InstStage, KindAndConstraint, Operand, OperandKind, OperandKindDefOnly, SSAVal,
+ SSAValDef,
},
- index::{BlockIdx, InstIdx, InstRange, SSAValIdx},
+ index::{BlockIdx, InstIdx, InstRange, OperandIdx, RangeIter, SSAValIdx},
interned::{GlobalState, Intern},
loc::Ty,
loc_set::LocSet,
let idom = self.func.blocks[block_idx].immediate_dominator;
self.available_ssa_vals_at_end(idom)
}
- fn new_ssa_val(ssa_vals: &mut Vec<SSAVal>, ty: Ty, def: SSAValDef) -> SSAValIdx {
+ fn new_ssa_val(ssa_vals: &mut Vec<SSAVal>, ty: Ty) -> SSAValIdx {
let retval = SSAValIdx::new(ssa_vals.len());
ssa_vals.push(SSAVal {
ty,
- def,
+ def: SSAValDef::invalid(),
operand_uses: Default::default(),
branch_succ_param_uses: Default::default(),
});
}
let mut possible_src_operand_idxs = vec![];
let mut possible_dest_operand_idxs = vec![];
- for (operand_idx, operand) in operands.iter().enumerate() {
+ for (operand_idx, operand) in operands.entries() {
match operand.kind_and_constraint.kind() {
OperandKind::Use => possible_src_operand_idxs.push(operand_idx),
OperandKind::Def => possible_dest_operand_idxs.push(operand_idx),
// src can have duplicates, so pick randomly
let possible_src_operand_idxs = (0..u.int_in_range(1..=3)?)
.map(|_| u.choose(&possible_src_operand_idxs).copied())
- .collect::<Result<Vec<usize>, Error>>()?;
+ .collect::<Result<Vec<OperandIdx>, Error>>()?;
// dest can't have duplicates, so shuffle
let len = possible_dest_operand_idxs.len();
for i in 0..len {
}
fn run(&mut self) -> Result<(), Error> {
let block_count = self.u.int_in_range(1..=10u16)?;
- for block_idx in 0..block_count as usize {
- let block_idx = BlockIdx::new(block_idx);
+ for block_idx in RangeIter::<BlockIdx>::from_usize_range(0..block_count as usize) {
let mut params = Vec::new();
if block_idx != BlockIdx::ENTRY_BLOCK {
- for param_idx in 0..self.u.int_in_range(0..=10)? {
+ for _param_idx in 0..self.u.int_in_range(0..=10)? {
let ty = self.u.arbitrary()?;
- params.push(Self::new_ssa_val(
- &mut self.func.ssa_vals,
- ty,
- SSAValDef::BlockParam {
- block: block_idx,
- param_idx,
- },
- ));
+ params.push(Self::new_ssa_val(&mut self.func.ssa_vals, ty));
}
}
let end = InstIdx::new(self.func.insts.len());
let kind = self.u.arbitrary()?;
let ssa_val = if let OperandKind::Def = kind {
let ty = self.u.arbitrary()?;
- let def = SSAValDef::Operand {
- inst: InstIdx::new(self.func.insts.len()),
- operand_idx: operands.len(),
- };
- Self::new_ssa_val(&mut self.func.ssa_vals, ty, def)
+ Self::new_ssa_val(&mut self.func.ssa_vals, ty)
} else {
SSAValIdx::new(!0)
};
self.new_inst_in_last_block(inst_kind, operands, clobbers);
}
}
- for block_idx in 0..self.func.blocks.len() {
- let block_idx = BlockIdx::new(block_idx);
+ for block_idx in self.func.blocks.keys() {
let term_idx = self
.func
.try_get_block_term_inst_idx(block_idx)
self.func.blocks[succ].preds.insert(block_idx);
}
}
- for block_idx in 0..self.func.blocks.len() {
- let block_idx = BlockIdx::new(block_idx);
+ for block_idx in self.func.blocks.keys() {
let term_idx = self
.func
.try_get_block_term_inst_idx(block_idx)
});
}
let dominators = dominators::simple_fast(&self.func, BlockIdx::ENTRY_BLOCK);
- for block_idx in 0..self.func.blocks.len() {
- let block_idx = BlockIdx::new(block_idx);
+ for block_idx in self.func.blocks.keys() {
self.func.blocks[block_idx].immediate_dominator =
dominators.immediate_dominator(block_idx);
}
// must have filled dominators first since available_ssa_vals_before_start() needs them
- for block_idx in 0..self.func.blocks.len() {
- let block_idx = BlockIdx::new(block_idx);
+ for block_idx in self.func.blocks.keys() {
let mut available_ssa_vals = self.available_ssa_vals_before_start(block_idx);
available_ssa_vals.extend(&self.func.ssa_vals, &self.func.blocks[block_idx].params);
for inst_idx in self.func.blocks[block_idx].insts {
let inst = &mut self.func.insts[inst_idx];
- for (operand_idx, operand) in inst.operands.iter_mut().enumerate() {
+ for (_operand_idx, operand) in inst.operands.entries_mut() {
if operand.kind_and_constraint.kind() != OperandKind::Use {
continue;
}
ssa_val: Self::new_ssa_val(
&mut self.func.ssa_vals,
self.u.arbitrary()?,
- SSAValDef::Operand {
- inst: inst_idx,
- operand_idx,
- },
),
stage: self.u.arbitrary()?,
};
} else {
operand.ssa_val = *self.u.choose(&available_ssa_vals.all)?;
}
- self.func.ssa_vals[operand.ssa_val]
- .operand_uses
- .insert(OperandUse {
- inst: inst_idx,
- operand_idx,
- });
}
available_ssa_vals.extend(
&self.func.ssa_vals,
InstKind::BlockTerm(block_term_inst_kind) => {
for (&succ_idx, params) in &mut block_term_inst_kind.succs_and_params {
let succ = &self.func.blocks[succ_idx];
- for (param_idx, &succ_param) in succ.params.iter().enumerate() {
+ for (_param_idx, &succ_param) in succ.params.entries() {
let succ_param_ty = self.func.ssa_vals[succ_param].ty;
let choices = available_ssa_vals.per_ty(succ_param_ty);
let ssa_val_idx;
if choices.is_empty() {
// no available ssa vals with correct type, fix that by appending def operand with that type
- ssa_val_idx = Self::new_ssa_val(
- &mut self.func.ssa_vals,
- succ_param_ty,
- SSAValDef::Operand {
- inst: inst_idx,
- operand_idx: inst.operands.len(),
- },
- );
+ ssa_val_idx =
+ Self::new_ssa_val(&mut self.func.ssa_vals, succ_param_ty);
available_ssa_vals.extend(&self.func.ssa_vals, [ssa_val_idx]);
- self.func.ssa_vals[ssa_val_idx].operand_uses.insert(
- OperandUse {
- inst: inst_idx,
- operand_idx: inst.operands.len(),
- },
- );
inst.operands.push(Operand {
kind_and_constraint: KindAndConstraint::Constraint {
kind: OperandKind::Def,
ssa_val_idx = *self.u.choose(choices)?
};
params.push(ssa_val_idx);
- self.func.ssa_vals[ssa_val_idx]
- .branch_succ_param_uses
- .insert(BranchSuccParamUse {
- branch_inst: inst_idx,
- succ: succ_idx,
- param_idx,
- });
}
}
}
}
}
}
- for (inst_idx, inst) in self.func.insts.iter_mut().enumerate() {
- let inst_idx = InstIdx::new(inst_idx);
- let mut reusable_operand_idxs: HashMap<Ty, Vec<usize>> = HashMap::new();
- for (operand_idx, operand) in inst.operands.iter().enumerate() {
+ for (inst_idx, inst) in self.func.insts.entries_mut() {
+ let mut reusable_operand_idxs: HashMap<Ty, Vec<OperandIdx>> = HashMap::new();
+ for (operand_idx, operand) in inst.operands.entries() {
if operand.kind_and_constraint.kind() == OperandKind::Use {
reusable_operand_idxs
.entry(self.func.ssa_vals[operand.ssa_val].ty)
}
}
let mut used_locs = EnumMap::<InstStage, LocSet>::default();
- for operand_idx in 0..inst.operands.len() {
+ for operand_idx in inst.operands.keys() {
let operand = &inst.operands[operand_idx];
let operand_ty = self.func.ssa_vals[operand.ssa_val].ty;
if operand.kind_and_constraint.kind() == OperandKind::Def && self.u.arbitrary()? {
- let reuse_operand_idx = self.u.choose_index(inst.operands.len())?;
- let reuse_operand = &inst.operands[reuse_operand_idx];
- if reuse_operand_idx != operand_idx
- && reuse_operand.kind_and_constraint.kind() == OperandKind::Use
- && self.func.ssa_vals[reuse_operand.ssa_val].ty == operand_ty
- {
- inst.operands[operand_idx].kind_and_constraint = KindAndConstraint::Reuse {
- kind: OperandKindDefOnly,
- reuse_operand_idx,
- };
- continue;
+ let reusable_operand_idxs = reusable_operand_idxs
+ .get(&operand_ty)
+ .map(Deref::deref)
+ .unwrap_or_default();
+ if !reusable_operand_idxs.is_empty() {
+ let reuse_operand_idx = *self.u.choose(reusable_operand_idxs)?;
+ if reuse_operand_idx != operand_idx {
+ inst.operands[operand_idx].kind_and_constraint =
+ KindAndConstraint::Reuse {
+ kind: OperandKindDefOnly,
+ reuse_operand_idx,
+ };
+ continue;
+ }
}
}
let operand = &mut inst.operands[operand_idx];
-use serde::{Deserialize, Serialize};
-use std::{fmt, iter::FusedIterator, ops::Range};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use std::{fmt, hash::Hash, iter::FusedIterator, ops::Range};
pub trait DisplayOptionIdx {
type Type: fmt::Display;
fn display_option_idx(self) -> Self::Type;
}
+pub trait IndexTy: Copy + fmt::Debug + Ord + Hash + Serialize + DeserializeOwned {
+ fn new(value: usize) -> Self;
+ fn get(self) -> usize;
+ fn next(self) -> Self {
+ Self::new(self.get() + 1)
+ }
+ fn prev(self) -> Self {
+ Self::new(self.get() - 1)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct RangeIter<T: IndexTy>(pub Range<T>);
+
+impl<T: IndexTy> RangeIter<T> {
+ pub fn as_usize_range(&self) -> Range<usize> {
+ self.0.start.get()..self.0.end.get()
+ }
+ pub fn from_usize_range(range: Range<usize>) -> Self {
+ Self(T::new(range.start)..T::new(range.end))
+ }
+ pub fn with_self_as_usize_range<R, F: FnOnce(&mut Range<usize>) -> R>(&mut self, f: F) -> R {
+ let mut range = self.as_usize_range();
+ let retval = f(&mut range);
+ *self = Self::from_usize_range(range);
+ retval
+ }
+}
+
+impl<T: IndexTy> Iterator for RangeIter<T> {
+ type Item = T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.with_self_as_usize_range(|r| r.next().map(T::new))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.as_usize_range().size_hint()
+ }
+
+ fn nth(&mut self, n: usize) -> Option<Self::Item> {
+ self.with_self_as_usize_range(|r| r.nth(n).map(T::new))
+ }
+
+ fn fold<B, F>(self, init: B, mut f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ self.as_usize_range()
+ .fold(init, move |a, v| f(a, T::new(v)))
+ }
+}
+
+impl<T: IndexTy> FusedIterator for RangeIter<T> {}
+
+impl<T: IndexTy> ExactSizeIterator for RangeIter<T> {
+ fn len(&self) -> usize {
+ self.as_usize_range().len()
+ }
+}
+
+impl<T: IndexTy> DoubleEndedIterator for RangeIter<T> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.with_self_as_usize_range(|r| r.next_back().map(T::new))
+ }
+
+ fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
+ self.with_self_as_usize_range(|r| r.nth_back(n).map(T::new))
+ }
+
+ fn rfold<B, F>(self, init: B, mut f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ self.as_usize_range()
+ .rfold(init, move |a, v| f(a, T::new(v)))
+ }
+}
+
macro_rules! define_index {
($name:ident) => {
#[derive(
value: usize,
}
+ impl IndexTy for $name {
+ fn new(value: usize) -> Self {
+ Self { value }
+ }
+ fn get(self) -> usize {
+ self.value
+ }
+ }
+
impl $name {
pub const fn new(value: usize) -> Self {
Self { value }
define_index!(SSAValIdx);
define_index!(InstIdx);
define_index!(BlockIdx);
+define_index!(BlockParamIdx);
+define_index!(OperandIdx);
impl fmt::Display for SSAValIdx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
}
+impl fmt::Display for BlockParamIdx {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "bparam{}", self.get())
+ }
+}
+
+impl fmt::Display for OperandIdx {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "operand{}", self.get())
+ }
+}
+
impl BlockIdx {
pub const ENTRY_BLOCK: BlockIdx = BlockIdx::new(0);
}