use super::{
arithmetic, bitwise, buffer, comparison, control, ir_specific, law, BinOpKind, Branch,
MutationError,
};
use crate::spec::types::DataType;
use crate::spec::types::MutationClass;
#[derive(Debug, Clone, PartialEq)]
pub enum Mutation {
ArithOpSwap {
from: BinOpKind,
to: BinOpKind,
},
WrappingAddToSaturatingAdd,
WrappingAddToCheckedAdd,
ConstantIncrement {
by: i64,
},
ConstantZeroToOne,
ConstantOneToZero,
ConstantMaxToZero,
IntegerConstantBitFlip {
bit: u8,
},
FloatConstantBitFlip {
bit: u8,
},
CompareOpSwap {
from: BinOpKind,
to: BinOpKind,
},
CompareInvert,
BitOpSwap {
from: BinOpKind,
to: BinOpKind,
},
BitNotDelete,
BitAndMaskDelete,
ControlFlowDelete {
branch: Branch,
},
ControlConditionInvert,
ControlLoopRangeEndIncrement,
ControlLoopRangeEndDecrement,
ControlLoopRangeStartIncrement,
ControlBreakToContinue,
ControlContinueRemove,
ControlFlowSkip,
BufferIndexShift {
by: i32,
},
BufferIndexClampFirst,
BufferIndexClampLast,
BufferReadWriteSwap,
AtomicOrderingWeaken,
AtomicOrderingAcqRelWeaken,
BufferCountShift {
by: i32,
},
IrBinOpSwap {
from: BinOpKind,
to: BinOpKind,
},
IrDataTypeSwap {
from: DataType,
to: DataType,
},
IrDeleteLaw {
op: &'static str,
},
IrSwapReferenceFn {
op: &'static str,
wrong_op: &'static str,
},
IrBufferAccessSwap,
IrRemoveValidationRule,
BytecodeConverterSwap {
from_opcode: u8,
to_opcode: u8,
},
IrWrongWgslOp,
IrWorkgroupSizeChange,
IrWorkgroupStrideMul {
factor: u32,
},
IrWorkgroupStrideDiv {
divisor: u32,
},
IrWorkgroupSizeMul {
factor: u32,
},
IrWorkgroupSizeDiv {
divisor: u32,
},
IrWorkgroupSizeOffset {
by: i32,
},
LowerRemoveBoundsCheck,
LowerRemoveShiftMask,
LawFalselyClaim {
law: &'static str,
op: &'static str,
},
LawIdentityCorrupt {
op: &'static str,
wrong: u32,
},
LawAbsorbingCorrupt {
op: &'static str,
wrong: u32,
},
}
#[must_use]
#[inline]
pub fn class_of(m: &Mutation) -> MutationClass {
use MutationClass as C;
match m {
Mutation::ArithOpSwap { .. }
| Mutation::WrappingAddToSaturatingAdd
| Mutation::WrappingAddToCheckedAdd => C::ArithmeticMutations,
Mutation::ConstantIncrement { .. }
| Mutation::ConstantZeroToOne
| Mutation::ConstantOneToZero
| Mutation::ConstantMaxToZero
| Mutation::IntegerConstantBitFlip { .. }
| Mutation::FloatConstantBitFlip { .. } => C::ConstantMutations,
Mutation::CompareOpSwap { .. } | Mutation::CompareInvert => C::ComparisonMutations,
Mutation::BitOpSwap { .. } | Mutation::BitNotDelete | Mutation::BitAndMaskDelete => {
C::BitwiseMutations
}
Mutation::ControlFlowDelete { .. }
| Mutation::ControlConditionInvert
| Mutation::ControlLoopRangeEndIncrement
| Mutation::ControlLoopRangeEndDecrement
| Mutation::ControlLoopRangeStartIncrement
| Mutation::ControlBreakToContinue
| Mutation::ControlContinueRemove
| Mutation::ControlFlowSkip => C::ControlFlowMutations,
Mutation::BufferIndexShift { .. }
| Mutation::BufferIndexClampFirst
| Mutation::BufferIndexClampLast
| Mutation::BufferReadWriteSwap
| Mutation::BufferCountShift { .. } => C::BufferAccessMutations,
Mutation::AtomicOrderingWeaken | Mutation::AtomicOrderingAcqRelWeaken => {
C::OrderingMutations
}
Mutation::IrBinOpSwap { .. }
| Mutation::IrDataTypeSwap { .. }
| Mutation::IrDeleteLaw { .. }
| Mutation::IrSwapReferenceFn { .. }
| Mutation::IrBufferAccessSwap
| Mutation::IrRemoveValidationRule
| Mutation::BytecodeConverterSwap { .. }
| Mutation::IrWrongWgslOp
| Mutation::IrWorkgroupSizeChange
| Mutation::IrWorkgroupStrideMul { .. }
| Mutation::IrWorkgroupStrideDiv { .. }
| Mutation::IrWorkgroupSizeMul { .. }
| Mutation::IrWorkgroupSizeDiv { .. }
| Mutation::IrWorkgroupSizeOffset { .. } => C::IrStructuralMutations,
Mutation::LowerRemoveBoundsCheck | Mutation::LowerRemoveShiftMask => C::LoweringMutations,
Mutation::LawFalselyClaim { .. }
| Mutation::LawIdentityCorrupt { .. }
| Mutation::LawAbsorbingCorrupt { .. } => C::LawMutations,
}
}
#[inline]
pub fn apply(source: &str, mutation: &Mutation) -> Result<String, MutationError> {
match mutation {
Mutation::ArithOpSwap { from, to } => Ok(arithmetic::apply_op_swap(source, *from, *to)),
Mutation::WrappingAddToSaturatingAdd => {
Ok(arithmetic::apply_wrapping_to_saturating(source))
}
Mutation::WrappingAddToCheckedAdd => Ok(arithmetic::apply_wrapping_to_checked(source)),
Mutation::ConstantIncrement { by } => Ok(arithmetic::apply_constant_increment(source, *by)),
Mutation::ConstantZeroToOne => Ok(arithmetic::apply_constant_zero_to_one(source)),
Mutation::ConstantOneToZero => Ok(arithmetic::apply_constant_one_to_zero(source)),
Mutation::ConstantMaxToZero => Ok(arithmetic::apply_constant_max_to_zero(source)),
Mutation::IntegerConstantBitFlip { bit } => {
Ok(arithmetic::apply_integer_constant_bit_flip(source, *bit))
}
Mutation::FloatConstantBitFlip { bit } => {
Ok(arithmetic::apply_float_constant_bit_flip(source, *bit))
}
Mutation::CompareOpSwap { from, to } => Ok(comparison::apply_op_swap(source, *from, *to)),
Mutation::CompareInvert => Ok(comparison::apply_invert(source)),
Mutation::BitOpSwap { from, to } => Ok(bitwise::apply_op_swap(source, *from, *to)),
Mutation::BitNotDelete => Ok(bitwise::apply_not_delete(source)),
Mutation::BitAndMaskDelete => Ok(bitwise::apply_and_mask_delete(source)),
Mutation::ControlFlowDelete { branch } => Ok(control::apply_branch_delete(source, *branch)),
Mutation::ControlConditionInvert => Ok(control::apply_condition_invert(source)),
Mutation::ControlLoopRangeEndIncrement => Ok(control::apply_loop_end_shift(source, 1)),
Mutation::ControlLoopRangeEndDecrement => Ok(control::apply_loop_end_shift(source, -1)),
Mutation::ControlLoopRangeStartIncrement => Ok(control::apply_loop_start_shift(source, 1)),
Mutation::ControlBreakToContinue => Ok(control::apply_break_to_continue(source)),
Mutation::ControlContinueRemove => Ok(control::apply_continue_remove(source)),
Mutation::ControlFlowSkip => Ok(control::apply_flow_skip(source)),
Mutation::BufferIndexShift { by } => Ok(buffer::apply_index_shift(source, *by)),
Mutation::BufferIndexClampFirst => Ok(buffer::apply_index_clamp(source, "0")),
Mutation::BufferIndexClampLast => Ok(buffer::apply_index_clamp(source, "len - 1")),
Mutation::BufferReadWriteSwap => Ok(buffer::apply_read_write_swap(source)),
Mutation::AtomicOrderingWeaken => Ok(buffer::apply_ordering_weaken(source)),
Mutation::AtomicOrderingAcqRelWeaken => Ok(buffer::apply_acqrel_ordering_weaken(source)),
Mutation::BufferCountShift { by } => Ok(buffer::apply_count_shift(source, *by)),
Mutation::IrBinOpSwap { from, to } => Ok(ir_specific::apply_binop_swap(source, *from, *to)),
Mutation::IrDataTypeSwap { from, to } => Ok(ir_specific::apply_datatype_swap(
source,
from.clone(),
to.clone(),
)),
Mutation::IrDeleteLaw { op } => Ok(ir_specific::apply_delete_law(source, op)),
Mutation::IrSwapReferenceFn { op, wrong_op } => {
Ok(ir_specific::apply_swap_reference_fn(source, op, wrong_op))
}
Mutation::IrBufferAccessSwap => Ok(ir_specific::apply_buffer_access_swap(source)),
Mutation::IrRemoveValidationRule => Ok(ir_specific::apply_remove_validation_rule(source)),
Mutation::BytecodeConverterSwap {
from_opcode,
to_opcode,
} => Ok(ir_specific::apply_bytecode_swap(
source,
*from_opcode,
*to_opcode,
)),
Mutation::IrWrongWgslOp => Ok(ir_specific::apply_wrong_wgsl_op(source)),
Mutation::IrWorkgroupSizeChange => Ok(ir_specific::apply_workgroup_size_change(source)),
Mutation::IrWorkgroupStrideMul { factor } => {
Ok(ir_specific::apply_workgroup_stride_mul(source, *factor))
}
Mutation::IrWorkgroupStrideDiv { divisor } => {
Ok(ir_specific::apply_workgroup_stride_div(source, *divisor))
}
Mutation::IrWorkgroupSizeMul { factor } => {
Ok(ir_specific::apply_workgroup_size_mul(source, *factor))
}
Mutation::IrWorkgroupSizeDiv { divisor } => {
Ok(ir_specific::apply_workgroup_size_div(source, *divisor))
}
Mutation::IrWorkgroupSizeOffset { by } => {
Ok(ir_specific::apply_workgroup_size_offset(source, *by))
}
Mutation::LowerRemoveBoundsCheck => Ok(ir_specific::apply_remove_bounds_check(source)),
Mutation::LowerRemoveShiftMask => Ok(ir_specific::apply_remove_shift_mask(source)),
Mutation::LawFalselyClaim { law, op } => Ok(law::apply_falsely_claim(source, law, op)),
Mutation::LawIdentityCorrupt { op, wrong } => {
Ok(law::apply_identity_corrupt(source, op, *wrong))
}
Mutation::LawAbsorbingCorrupt { op, wrong } => {
Ok(law::apply_absorbing_corrupt(source, op, *wrong))
}
}
}
#[must_use]
#[inline]
pub fn mutations_for(class: MutationClass) -> Vec<Mutation> {
super::catalog::MUTATION_CATALOG
.iter()
.filter(|m| class_of(m) == class)
.cloned()
.collect()
}