Remove IFLAGS/FFLAGS types (#5406)

All instructions using the CPU flags types (IFLAGS/FFLAGS) were already
removed.  This patch completes the cleanup by removing all remaining
instructions that define values of CPU flags types, as well as the
types themselves.

Specifically, the following features are removed:
- The IFLAGS and FFLAGS types and the SpecialType category.
- Special handling of IFLAGS and FFLAGS in machinst/isle.rs and
  machinst/lower.rs.
- The ifcmp, ifcmp_imm, ffcmp, iadd_ifcin, iadd_ifcout, iadd_ifcarry,
  isub_ifbin, isub_ifbout, and isub_ifborrow instructions.
- The writes_cpu_flags instruction property.
- The flags verifier pass.
- Flags handling in the interpreter.

All of these features are currently unused; no functional change
intended by this patch.

This addresses https://github.com/bytecodealliance/wasmtime/issues/3249.
This commit is contained in:
Ulrich Weigand
2022-12-09 22:42:03 +01:00
committed by GitHub
parent 6e0a029c35
commit e913cf3647
42 changed files with 55 additions and 1119 deletions

View File

@@ -73,8 +73,6 @@ pub(crate) struct InstructionContent {
pub can_trap: bool,
/// Does this instruction have other side effects besides can_* flags?
pub other_side_effects: bool,
/// Does this instruction write to CPU flags?
pub writes_cpu_flags: bool,
}
impl InstructionContent {
@@ -240,9 +238,6 @@ impl InstructionBuilder {
let polymorphic_info =
verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
// Infer from output operands whether an instruction clobbers CPU flags or not.
let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
let camel_name = camel_case(&self.name);
Rc::new(InstructionContent {
@@ -264,7 +259,6 @@ impl InstructionBuilder {
can_store: self.can_store,
can_trap: self.can_trap,
other_side_effects: self.other_side_effects,
writes_cpu_flags,
})
}
}

View File

@@ -87,17 +87,6 @@ impl Operand {
_ => false,
}
}
pub fn is_cpu_flags(&self) -> bool {
match &self.kind.fields {
OperandKindFields::TypeVar(type_var)
if type_var.name == "iflags" || type_var.name == "fflags" =>
{
true
}
_ => false,
}
}
}
pub type EnumValues = HashMap<&'static str, &'static str>;

View File

@@ -18,7 +18,6 @@ static RUST_NAME_PREFIX: &str = "ir::types::";
pub(crate) enum ValueType {
Lane(LaneType),
Reference(ReferenceType),
Special(SpecialType),
Vector(VectorType),
DynamicVector(DynamicVectorType),
}
@@ -29,11 +28,6 @@ impl ValueType {
LaneTypeIterator::new()
}
/// Iterate through all of the special types (neither lanes nor vectors).
pub fn all_special_types() -> SpecialTypeIterator {
SpecialTypeIterator::new()
}
pub fn all_reference_types() -> ReferenceTypeIterator {
ReferenceTypeIterator::new()
}
@@ -43,7 +37,6 @@ impl ValueType {
match *self {
ValueType::Lane(l) => l.doc(),
ValueType::Reference(r) => r.doc(),
ValueType::Special(s) => s.doc(),
ValueType::Vector(ref v) => v.doc(),
ValueType::DynamicVector(ref v) => v.doc(),
}
@@ -54,7 +47,6 @@ impl ValueType {
match *self {
ValueType::Lane(l) => l.lane_bits(),
ValueType::Reference(r) => r.lane_bits(),
ValueType::Special(s) => s.lane_bits(),
ValueType::Vector(ref v) => v.lane_bits(),
ValueType::DynamicVector(ref v) => v.lane_bits(),
}
@@ -78,7 +70,6 @@ impl ValueType {
match *self {
ValueType::Lane(l) => l.number(),
ValueType::Reference(r) => r.number(),
ValueType::Special(s) => s.number(),
ValueType::Vector(ref v) => v.number(),
ValueType::DynamicVector(ref v) => v.number(),
}
@@ -100,7 +91,6 @@ impl fmt::Display for ValueType {
match *self {
ValueType::Lane(l) => l.fmt(f),
ValueType::Reference(r) => r.fmt(f),
ValueType::Special(s) => s.fmt(f),
ValueType::Vector(ref v) => v.fmt(f),
ValueType::DynamicVector(ref v) => v.fmt(f),
}
@@ -121,13 +111,6 @@ impl From<ReferenceType> for ValueType {
}
}
/// Create a ValueType from a given special type.
impl From<SpecialType> for ValueType {
fn from(spec: SpecialType) -> Self {
ValueType::Special(spec)
}
}
/// Create a ValueType from a given vector type.
impl From<VectorType> for ValueType {
fn from(vector: VectorType) -> Self {
@@ -436,91 +419,6 @@ impl fmt::Debug for DynamicVectorType {
}
}
/// A concrete scalar type that is neither a vector nor a lane type.
///
/// Special types cannot be used to form vectors.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum SpecialType {
Flag(shared_types::Flag),
}
impl SpecialType {
/// Return a string containing the documentation comment for this special type.
pub fn doc(self) -> String {
match self {
SpecialType::Flag(shared_types::Flag::IFlags) => String::from(
"CPU flags representing the result of an integer comparison. These flags
can be tested with an :type:`intcc` condition code.",
),
SpecialType::Flag(shared_types::Flag::FFlags) => String::from(
"CPU flags representing the result of a floating point comparison. These
flags can be tested with a :type:`floatcc` condition code.",
),
}
}
/// Return the number of bits in a lane.
pub fn lane_bits(self) -> u64 {
match self {
SpecialType::Flag(_) => 0,
}
}
/// Find the unique number associated with this special type.
pub fn number(self) -> u16 {
match self {
SpecialType::Flag(shared_types::Flag::IFlags) => 1,
SpecialType::Flag(shared_types::Flag::FFlags) => 2,
}
}
}
impl fmt::Display for SpecialType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SpecialType::Flag(shared_types::Flag::IFlags) => write!(f, "iflags"),
SpecialType::Flag(shared_types::Flag::FFlags) => write!(f, "fflags"),
}
}
}
impl fmt::Debug for SpecialType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
SpecialType::Flag(_) => format!("FlagsType({})", self),
}
)
}
}
impl From<shared_types::Flag> for SpecialType {
fn from(f: shared_types::Flag) -> Self {
SpecialType::Flag(f)
}
}
pub(crate) struct SpecialTypeIterator {
flag_iter: shared_types::FlagIterator,
}
impl SpecialTypeIterator {
fn new() -> Self {
Self {
flag_iter: shared_types::FlagIterator::new(),
}
}
}
impl Iterator for SpecialTypeIterator {
type Item = SpecialType;
fn next(&mut self) -> Option<Self::Item> {
self.flag_iter.next().map(SpecialType::from)
}
}
/// Reference type is scalar type, but not lane type.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ReferenceType(pub shared_types::Reference);

View File

@@ -6,7 +6,7 @@ use std::iter::FromIterator;
use std::ops;
use std::rc::Rc;
use crate::cdsl::types::{LaneType, ReferenceType, SpecialType, ValueType};
use crate::cdsl::types::{LaneType, ReferenceType, ValueType};
const MAX_LANES: u16 = 256;
const MAX_BITS: u16 = 128;
@@ -57,9 +57,6 @@ impl TypeVar {
let mut builder = TypeSetBuilder::new();
let (scalar_type, num_lanes) = match value_type {
ValueType::Special(special_type) => {
return TypeVar::new(name, doc, builder.specials(vec![special_type]).build());
}
ValueType::Reference(ReferenceType(reference_type)) => {
let bits = reference_type as RangeBound;
return TypeVar::new(name, doc, builder.refs(bits..bits).build());
@@ -156,7 +153,6 @@ impl TypeVar {
let ts = self.get_typeset();
// Safety checks to avoid over/underflows.
assert!(ts.specials.is_empty(), "can't derive from special types");
match derived_func {
DerivedFunc::HalfWidth => {
assert!(
@@ -364,9 +360,6 @@ pub(crate) struct TypeVarParent {
/// - The permitted range of boolean types.
///
/// The ranges are inclusive from smallest bit-width to largest bit-width.
///
/// Finally, a type set can contain special types (derived from `SpecialType`)
/// which can't appear as lane types.
type RangeBound = u16;
type Range = ops::Range<RangeBound>;
@@ -385,7 +378,6 @@ pub(crate) struct TypeSet {
pub ints: NumSet,
pub floats: NumSet,
pub refs: NumSet,
pub specials: Vec<SpecialType>,
}
impl TypeSet {
@@ -395,7 +387,6 @@ impl TypeSet {
ints: NumSet,
floats: NumSet,
refs: NumSet,
specials: Vec<SpecialType>,
) -> Self {
Self {
lanes,
@@ -403,7 +394,6 @@ impl TypeSet {
ints,
floats,
refs,
specials,
}
}
@@ -411,7 +401,6 @@ impl TypeSet {
pub fn size(&self) -> usize {
self.lanes.len() * (self.ints.len() + self.floats.len() + self.refs.len())
+ self.dynamic_lanes.len() * (self.ints.len() + self.floats.len() + self.refs.len())
+ self.specials.len()
}
/// Return the image of self across the derived function func.
@@ -450,7 +439,6 @@ impl TypeSet {
let mut copy = self.clone();
copy.ints = NumSet::from_iter(self.ints.iter().filter(|&&x| x > 8).map(|&x| x / 2));
copy.floats = NumSet::from_iter(self.floats.iter().filter(|&&x| x > 32).map(|&x| x / 2));
copy.specials = Vec::new();
copy
}
@@ -464,7 +452,6 @@ impl TypeSet {
.filter(|&&x| x < MAX_FLOAT_BITS)
.map(|&x| x * 2),
);
copy.specials = Vec::new();
copy
}
@@ -472,7 +459,6 @@ impl TypeSet {
fn half_vector(&self) -> TypeSet {
let mut copy = self.clone();
copy.lanes = NumSet::from_iter(self.lanes.iter().filter(|&&x| x > 1).map(|&x| x / 2));
copy.specials = Vec::new();
copy
}
@@ -485,7 +471,6 @@ impl TypeSet {
.filter(|&&x| x < MAX_LANES)
.map(|&x| x * 2),
);
copy.specials = Vec::new();
copy
}
@@ -497,7 +482,6 @@ impl TypeSet {
.filter(|&&x| x < MAX_LANES)
.map(|&x| x),
);
copy.specials = Vec::new();
copy.dynamic_lanes = NumSet::new();
copy
}
@@ -523,9 +507,6 @@ impl TypeSet {
ret.push(LaneType::float_from_bits(bits).to_dynamic(num_lanes));
}
}
for &special in &self.specials {
ret.push(special.into());
}
ret
}
@@ -572,12 +553,6 @@ impl fmt::Debug for TypeSet {
Vec::from_iter(self.refs.iter().map(|x| x.to_string())).join(", ")
));
}
if !self.specials.is_empty() {
subsets.push(format!(
"specials={{{}}}",
Vec::from_iter(self.specials.iter().map(|x| x.to_string())).join(", ")
));
}
write!(fmt, "{})", subsets.join(", "))?;
Ok(())
@@ -591,7 +566,6 @@ pub(crate) struct TypeSetBuilder {
includes_scalars: bool,
simd_lanes: Interval,
dynamic_simd_lanes: Interval,
specials: Vec<SpecialType>,
}
impl TypeSetBuilder {
@@ -603,7 +577,6 @@ impl TypeSetBuilder {
includes_scalars: true,
simd_lanes: Interval::None,
dynamic_simd_lanes: Interval::None,
specials: Vec::new(),
}
}
@@ -636,11 +609,6 @@ impl TypeSetBuilder {
self.dynamic_simd_lanes = interval.into();
self
}
pub fn specials(mut self, specials: Vec<SpecialType>) -> Self {
assert!(self.specials.is_empty());
self.specials = specials;
self
}
pub fn build(self) -> TypeSet {
let min_lanes = if self.includes_scalars { 1 } else { 2 };
@@ -651,7 +619,6 @@ impl TypeSetBuilder {
range_to_set(self.ints.to_range(8..MAX_BITS, None)),
range_to_set(self.floats.to_range(32..64, None)),
range_to_set(self.refs.to_range(32..64, None)),
self.specials,
)
}
}
@@ -721,13 +688,11 @@ fn test_typevar_builder() {
assert_eq!(type_set.lanes, num_set![1]);
assert!(type_set.floats.is_empty());
assert_eq!(type_set.ints, num_set![8, 16, 32, 64, 128]);
assert!(type_set.specials.is_empty());
let type_set = TypeSetBuilder::new().floats(Interval::All).build();
assert_eq!(type_set.lanes, num_set![1]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert!(type_set.ints.is_empty());
assert!(type_set.specials.is_empty());
let type_set = TypeSetBuilder::new()
.floats(Interval::All)
@@ -737,7 +702,6 @@ fn test_typevar_builder() {
assert_eq!(type_set.lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert!(type_set.ints.is_empty());
assert!(type_set.specials.is_empty());
let type_set = TypeSetBuilder::new()
.floats(Interval::All)
@@ -747,7 +711,6 @@ fn test_typevar_builder() {
assert_eq!(type_set.lanes, num_set![1, 2, 4, 8, 16, 32, 64, 128, 256]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert!(type_set.ints.is_empty());
assert!(type_set.specials.is_empty());
let type_set = TypeSetBuilder::new()
.floats(Interval::All)
@@ -758,7 +721,6 @@ fn test_typevar_builder() {
assert_eq!(type_set.floats, num_set![32, 64]);
assert!(type_set.dynamic_lanes.is_empty());
assert!(type_set.ints.is_empty());
assert!(type_set.specials.is_empty());
let type_set = TypeSetBuilder::new()
.ints(Interval::All)
@@ -773,7 +735,6 @@ fn test_typevar_builder() {
assert_eq!(type_set.ints, num_set![8, 16, 32, 64, 128]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.lanes, num_set![1]);
assert!(type_set.specials.is_empty());
let type_set = TypeSetBuilder::new()
.floats(Interval::All)
@@ -787,13 +748,11 @@ fn test_typevar_builder() {
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.lanes, num_set![1]);
assert!(type_set.ints.is_empty());
assert!(type_set.specials.is_empty());
let type_set = TypeSetBuilder::new().ints(16..64).build();
assert_eq!(type_set.lanes, num_set![1]);
assert_eq!(type_set.ints, num_set![16, 32, 64]);
assert!(type_set.floats.is_empty());
assert!(type_set.specials.is_empty());
}
#[test]
@@ -979,7 +938,6 @@ fn test_typevar_singleton() {
assert_eq!(typevar.name, "i32");
assert_eq!(typevar.type_set.ints, num_set![32]);
assert!(typevar.type_set.floats.is_empty());
assert!(typevar.type_set.specials.is_empty());
assert_eq!(typevar.type_set.lanes, num_set![1]);
// Test f32x4.
@@ -991,5 +949,4 @@ fn test_typevar_singleton() {
assert!(typevar.type_set.ints.is_empty());
assert_eq!(typevar.type_set.floats, num_set![32]);
assert_eq!(typevar.type_set.lanes, num_set![4]);
assert!(typevar.type_set.specials.is_empty());
}

View File

@@ -519,13 +519,6 @@ fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
"Does this instruction have other side effects besides can_* flags?",
fmt,
);
gen_bool_accessor(
all_inst,
|inst| inst.writes_cpu_flags,
"writes_cpu_flags",
"Does this instruction write to CPU flags?",
fmt,
);
});
fmt.line("}");
fmt.empty_line();
@@ -652,9 +645,6 @@ fn typeset_to_string(ts: &TypeSet) -> String {
if !ts.floats.is_empty() {
result += &format!(", floats={}", iterable_to_string(&ts.floats));
}
if !ts.specials.is_empty() {
result += &format!(", specials=[{}]", iterable_to_string(&ts.specials));
}
if !ts.refs.is_empty() {
result += &format!(", refs={}", iterable_to_string(&ts.refs));
}

View File

@@ -48,11 +48,6 @@ fn emit_dynamic_vectors(bits: u64, fmt: &mut srcgen::Formatter) {
/// Emit types using the given formatter object.
fn emit_types(fmt: &mut srcgen::Formatter) {
// Emit all of the special types, such as types for CPU flags.
for spec in cdsl_types::ValueType::all_special_types().map(cdsl_types::ValueType::from) {
emit_type(&spec, fmt);
}
// Emit all of the lane types, such integers, floats, and booleans.
for ty in cdsl_types::ValueType::all_lane_types().map(cdsl_types::ValueType::from) {
emit_type(&ty, fmt);

View File

@@ -571,9 +571,6 @@ pub(crate) fn define(
define_simd_arithmetic(&mut ig, formats, imm, entities);
// Operand kind shorthands.
let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into();
let fflags: &TypeVar = &ValueType::Special(types::Flag::FFlags.into()).into();
let i8: &TypeVar = &ValueType::from(LaneType::from(types::Int::I8)).into();
let f32_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F32)).into();
let f64_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F64)).into();
@@ -1671,40 +1668,6 @@ pub(crate) fn define(
.operands_out(vec![a]),
);
let f = &Operand::new("f", iflags);
let x = &Operand::new("x", iB);
let y = &Operand::new("y", iB);
ig.push(
Inst::new(
"ifcmp",
r#"
Compare scalar integers and return flags.
Compare two scalar integer values and return integer CPU flags
representing the result.
"#,
&formats.binary,
)
.operands_in(vec![x, y])
.operands_out(vec![f]),
);
ig.push(
Inst::new(
"ifcmp_imm",
r#"
Compare scalar integer to a constant and return flags.
Like `icmp_imm`, but returns integer CPU flags instead of testing
a specific condition code.
"#,
&formats.binary_imm64,
)
.operands_in(vec![x, Y])
.operands_out(vec![f]),
);
let a = &Operand::new("a", Int);
let x = &Operand::new("x", Int);
let y = &Operand::new("y", Int);
@@ -2043,11 +2006,6 @@ pub(crate) fn define(
let b_in = &Operand::new("b_in", i8).with_doc("Input borrow flag");
let b_out = &Operand::new("b_out", i8).with_doc("Output borrow flag");
let c_if_in = &Operand::new("c_in", iflags);
let c_if_out = &Operand::new("c_out", iflags);
let b_if_in = &Operand::new("b_in", iflags);
let b_if_out = &Operand::new("b_out", iflags);
ig.push(
Inst::new(
"iadd_cin",
@@ -2069,27 +2027,6 @@ pub(crate) fn define(
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"iadd_ifcin",
r#"
Add integers with carry in.
Same as `iadd` with an additional carry flag input. Computes:
```text
a = x + y + c_{in} \pmod 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
&formats.ternary,
)
.operands_in(vec![x, y, c_if_in])
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"iadd_cout",
@@ -2112,28 +2049,6 @@ pub(crate) fn define(
.operands_out(vec![a, c_out]),
);
ig.push(
Inst::new(
"iadd_ifcout",
r#"
Add integers with carry out.
Same as `iadd` with an additional carry flag output.
```text
a &= x + y \pmod 2^B \\
c_{out} &= x+y >= 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
&formats.binary,
)
.operands_in(vec![x, y])
.operands_out(vec![a, c_if_out]),
);
ig.push(
Inst::new(
"iadd_carry",
@@ -2156,28 +2071,6 @@ pub(crate) fn define(
.operands_out(vec![a, c_out]),
);
ig.push(
Inst::new(
"iadd_ifcarry",
r#"
Add integers with carry in and out.
Same as `iadd` with an additional carry flag input and output.
```text
a &= x + y + c_{in} \pmod 2^B \\
c_{out} &= x + y + c_{in} >= 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
&formats.ternary,
)
.operands_in(vec![x, y, c_if_in])
.operands_out(vec![a, c_if_out]),
);
{
let code = &Operand::new("code", &imm.trapcode);
@@ -2227,27 +2120,6 @@ pub(crate) fn define(
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"isub_ifbin",
r#"
Subtract integers with borrow in.
Same as `isub` with an additional borrow flag input. Computes:
```text
a = x - (y + b_{in}) \pmod 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
&formats.ternary,
)
.operands_in(vec![x, y, b_if_in])
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"isub_bout",
@@ -2270,28 +2142,6 @@ pub(crate) fn define(
.operands_out(vec![a, b_out]),
);
ig.push(
Inst::new(
"isub_ifbout",
r#"
Subtract integers with borrow out.
Same as `isub` with an additional borrow flag output.
```text
a &= x - y \pmod 2^B \\
b_{out} &= x < y
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
&formats.binary,
)
.operands_in(vec![x, y])
.operands_out(vec![a, b_if_out]),
);
ig.push(
Inst::new(
"isub_borrow",
@@ -2314,28 +2164,6 @@ pub(crate) fn define(
.operands_out(vec![a, b_out]),
);
ig.push(
Inst::new(
"isub_ifborrow",
r#"
Subtract integers with borrow in and out.
Same as `isub` with an additional borrow flag input and output.
```text
a &= x - (y + b_{in}) \pmod 2^B \\
b_{out} &= x < y + b_{in}
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
&formats.ternary,
)
.operands_in(vec![x, y, b_if_in])
.operands_out(vec![a, b_if_out]),
);
let bits = &TypeVar::new(
"bits",
"Any integer, float, or vector type",
@@ -2848,23 +2676,6 @@ pub(crate) fn define(
.operands_out(vec![a]),
);
let f = &Operand::new("f", fflags);
ig.push(
Inst::new(
"ffcmp",
r#"
Floating point comparison returning flags.
Compares two numbers like `fcmp`, but returns floating point CPU
flags instead of testing a specific condition.
"#,
&formats.binary,
)
.operands_in(vec![x, y])
.operands_out(vec![f]),
);
let x = &Operand::new("x", Float);
let y = &Operand::new("y", Float);
let z = &Operand::new("z", Float);

View File

@@ -72,41 +72,6 @@ impl Iterator for FloatIterator {
}
}
/// A type representing CPU flags.
///
/// Flags can't be stored in memory.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub(crate) enum Flag {
/// CPU flags from an integer comparison.
IFlags,
/// CPU flags from a floating point comparison.
FFlags,
}
/// Iterator through the variants of the Flag enum.
pub(crate) struct FlagIterator {
index: u8,
}
impl FlagIterator {
pub fn new() -> Self {
Self { index: 0 }
}
}
impl Iterator for FlagIterator {
type Item = Flag;
fn next(&mut self) -> Option<Self::Item> {
let res = match self.index {
0 => Some(Flag::IFlags),
1 => Some(Flag::FFlags),
_ => return None,
};
self.index += 1;
res
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub(crate) enum Reference {
/// 32-bit reference.
@@ -162,14 +127,6 @@ mod iter_tests {
assert_eq!(float_iter.next(), None);
}
#[test]
fn flag_iter_works() {
let mut flag_iter = FlagIterator::new();
assert_eq!(flag_iter.next(), Some(Flag::IFlags));
assert_eq!(flag_iter.next(), Some(Flag::FFlags));
assert_eq!(flag_iter.next(), None);
}
#[test]
fn reference_iter_works() {
let mut reference_iter = ReferenceIterator::new();