public void Generate()
        {
            var filename = genTypes.Dirs.GetRustFilename("instruction_internal.rs");

            new FileUpdater(TargetLanguage.Rust, "RegToAddrSize", filename).Generate(writer => {
                var registerType = genTypes[TypeIds.Register];

                var icedConstantsName = genTypes.GetConstantsType(TypeIds.IcedConstants).Name(idConverter);
                var regCountName      = idConverter.Constant(IcedConstants.GetEnumCountName(TypeIds.Register));
                writer.WriteLine(RustConstants.AttributeNoRustFmt);
                writer.WriteLine($"static REG_TO_ADDR_SIZE: [u8; {icedConstantsName}::{regCountName}] = [");
                using (writer.Indent()) {
                    foreach (var regEnum in registerType.Values)
                    {
                        var reg  = (Register)regEnum.Value;
                        var size = GetAddrSize(reg);
                        if (size > byte.MaxValue)
                        {
                            throw new InvalidOperationException();
                        }
                        writer.WriteLine($"{size}, // {regEnum.Name(idConverter)}");
                    }
                }
                writer.WriteLine("];");
            });
        }
        public void Generate()
        {
            var genTypes      = generatorContext.Types;
            var icedConstants = genTypes.GetConstantsType(TypeIds.IcedConstants);
            var defs          = genTypes.GetObject <InstructionDefs>(TypeIds.InstructionDefs).Defs;
            var mnemonicName  = genTypes[TypeIds.Mnemonic].Name(idConverter);

            using (var writer = new FileWriter(TargetLanguage.Rust, FileUtils.OpenWrite(generatorContext.Types.Dirs.GetRustFilename("mnemonics.rs")))) {
                writer.WriteFileHeader();

                writer.WriteLine($"use crate::iced_constants::{icedConstants.Name(idConverter)};");
                writer.WriteLine($"use crate::{genTypes[TypeIds.Mnemonic].Name(idConverter)};");
                writer.WriteLine();
                writer.WriteLine(RustConstants.AttributeNoRustFmt);
                writer.WriteLine($"pub(super) static TO_MNEMONIC: [{mnemonicName}; {icedConstants.Name(idConverter)}::{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)}] = [");
                using (writer.Indent()) {
                    foreach (var def in defs)
                    {
                        if (def.Mnemonic.Value > ushort.MaxValue)
                        {
                            throw new InvalidOperationException();
                        }
                        writer.WriteLine($"{idConverter.ToDeclTypeAndValue(def.Mnemonic)},// {def.Code.Name(idConverter)}");
                    }
                }
                writer.WriteLine("];");
            }
        }
        public void Generate()
        {
            var          icedConstants = genTypes.GetConstantsType(TypeIds.IcedConstants);
            var          defs          = genTypes.GetObject <InstructionDefs>(TypeIds.InstructionDefs).Defs;
            const string ClassName     = "InstructionMemorySizes";
            var          memSizeName   = genTypes[TypeIds.MemorySize].Name(idConverter);

            using (var writer = new FileWriter(TargetLanguage.CSharp, FileUtils.OpenWrite(CSharpConstants.GetFilename(genTypes, CSharpConstants.IcedNamespace, ClassName + ".g.cs")))) {
                writer.WriteFileHeader();

                writer.WriteLine($"namespace {CSharpConstants.IcedNamespace} {{");
                using (writer.Indent()) {
                    writer.WriteLine($"static class {ClassName} {{");
                    using (writer.Indent()) {
                        writer.WriteCommentLine("0 = memory size");
                        writer.WriteCommentLine("1 = broadcast memory size");
                        writer.WriteLineNoIndent($"#if {CSharpConstants.HasSpanDefine}");
                        writer.WriteLine($"internal static System.ReadOnlySpan<byte> Sizes => new byte[{icedConstants.Name(idConverter)}.{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)} * 2] {{");
                        writer.WriteLineNoIndent("#else");
                        writer.WriteLine($"internal static readonly byte[] Sizes = new byte[{icedConstants.Name(idConverter)}.{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)} * 2] {{");
                        writer.WriteLineNoIndent("#endif");
                        using (writer.Indent()) {
                            foreach (var def in defs)
                            {
                                if (def.Memory.Value > byte.MaxValue)
                                {
                                    throw new InvalidOperationException();
                                }
                                string value;
                                if (def.Memory.Value == 0)
                                {
                                    value = "0";
                                }
                                else
                                {
                                    value = $"(byte){memSizeName}.{def.Memory.Name(idConverter)}";
                                }
                                writer.WriteLine($"{value},// {def.Code.Name(idConverter)}");
                            }
                            foreach (var def in defs)
                            {
                                if (def.MemoryBroadcast.Value > byte.MaxValue)
                                {
                                    throw new InvalidOperationException();
                                }
                                string value;
                                if (def.MemoryBroadcast.Value == 0)
                                {
                                    value = "0";
                                }
                                else
                                {
                                    value = $"(byte){memSizeName}.{def.MemoryBroadcast.Name(idConverter)}";
                                }
                                writer.WriteLine($"{value},// {def.Code.Name(idConverter)}");
                            }
                        }
                        writer.WriteLine("};");
                    }
                    writer.WriteLine("}");
                }
                writer.WriteLine("}");
            }
        }
        public void Generate()
        {
            var genTypes      = generatorContext.Types;
            var icedConstants = genTypes.GetConstantsType(TypeIds.IcedConstants);
            var defs          = genTypes.GetObject <InstructionDefs>(TypeIds.InstructionDefs).Defs;
            var memSizeName   = genTypes[TypeIds.MemorySize].Name(idConverter);

            using (var writer = new FileWriter(TargetLanguage.Rust, FileUtils.OpenWrite(generatorContext.Types.Dirs.GetRustFilename("instruction_memory_sizes.rs")))) {
                writer.WriteFileHeader();
                writer.WriteLine($"use crate::iced_constants::{icedConstants.Name(idConverter)};");
                writer.WriteLine($"use crate::{genTypes[TypeIds.MemorySize].Name(idConverter)};");
                writer.WriteLine();
                writer.WriteLine(RustConstants.AttributeNoRustFmt);
                writer.WriteLine($"pub(super) static SIZES_NORMAL: [{memSizeName}; {icedConstants.Name(idConverter)}::{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)}] = [");
                using (writer.Indent()) {
                    foreach (var def in defs)
                    {
                        if (def.Memory.Value > byte.MaxValue)
                        {
                            throw new InvalidOperationException();
                        }
                        string value = $"{memSizeName}::{def.Memory.Name(idConverter)}";
                        writer.WriteLine($"{value},// {def.Code.Name(idConverter)}");
                    }
                }
                writer.WriteLine("];");
                writer.WriteLine();
                writer.WriteLine(RustConstants.AttributeNoRustFmt);
                writer.WriteLine($"pub(super) static SIZES_BCST: [{memSizeName}; {icedConstants.Name(idConverter)}::{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)}] = [");
                using (writer.Indent()) {
                    foreach (var def in defs)
                    {
                        if (def.MemoryBroadcast.Value > byte.MaxValue)
                        {
                            throw new InvalidOperationException();
                        }
                        string value = $"{memSizeName}::{def.MemoryBroadcast.Name(idConverter)}";
                        writer.WriteLine($"{value},// {def.Code.Name(idConverter)}");
                    }
                }
                writer.WriteLine("];");
            }
        }
        public void Generate()
        {
            var          icedConstants = genTypes.GetConstantsType(TypeIds.IcedConstants);
            var          defs          = genTypes.GetObject <InstructionDefs>(TypeIds.InstructionDefs).Defs;
            const string ClassName     = "MnemonicUtilsData";
            var          mnemonicName  = genTypes[TypeIds.Mnemonic].Name(idConverter);

            using (var writer = new FileWriter(TargetLanguage.CSharp, FileUtils.OpenWrite(CSharpConstants.GetFilename(genTypes, CSharpConstants.IcedNamespace, ClassName + ".g.cs")))) {
                writer.WriteFileHeader();

                writer.WriteLine($"namespace {CSharpConstants.IcedNamespace} {{");
                using (writer.Indent()) {
                    writer.WriteLine($"static class {ClassName} {{");
                    using (writer.Indent()) {
                        writer.WriteLine($"internal static readonly ushort[] toMnemonic = new ushort[{icedConstants.Name(idConverter)}.{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)}] {{");
                        using (writer.Indent()) {
                            foreach (var def in defs)
                            {
                                if (def.Mnemonic.Value > ushort.MaxValue)
                                {
                                    throw new InvalidOperationException();
                                }
                                writer.WriteLine($"(ushort){mnemonicName}.{def.Mnemonic.Name(idConverter)},// {def.Code.Name(idConverter)}");
                            }
                        }
                        writer.WriteLine("};");
                    }
                    writer.WriteLine("}");
                }
                writer.WriteLine("}");
            }
        }
示例#6
0
        void WriteEnumCore(FileWriter writer, PartialEnumFileInfo info, EnumType enumType)
        {
            docWriter.WriteSummary(writer, enumType.Documentation, enumType.RawName);
            var enumTypeName = enumType.Name(idConverter);

            foreach (var attr in info.Attributes)
            {
                writer.WriteLine(attr);
            }
            if (enumType.IsPublic && enumType.IsMissingDocs)
            {
                writer.WriteLine(RustConstants.AttributeAllowMissingDocs);
            }
            if (!enumType.IsPublic)
            {
                writer.WriteLine(RustConstants.AttributeAllowDeadCode);
            }
            var pub = enumType.IsPublic ? "pub " : "pub(crate) ";

            writer.WriteLine($"{pub}enum {enumTypeName} {{");
            // Identical enum values aren't allowed so just remove them
            var enumValues = enumType.Values.Where(a => !a.DeprecatedInfo.IsDeprecatedAndRenamed).ToArray();

            using (writer.Indent()) {
                uint expectedValue = 0;
                foreach (var value in enumValues)
                {
                    docWriter.WriteSummary(writer, value.Documentation, enumType.RawName);
                    deprecatedWriter.WriteDeprecated(writer, value);
                    if (expectedValue != value.Value || enumType.IsPublic)
                    {
                        writer.WriteLine($"{value.Name(idConverter)} = {value.Value},");
                    }
                    else
                    {
                        writer.WriteLine($"{value.Name(idConverter)},");
                    }
                    expectedValue = value.Value + 1;
                }
            }
            writer.WriteLine("}");

            var arrayName = idConverter.Constant("GenDebug" + enumType.RawName);
            var feature   = info.Attributes.FirstOrDefault(a => a.StartsWith(RustConstants.FeaturePrefix, StringComparison.Ordinal) && a.Contains("(feature", StringComparison.Ordinal));

            if (feature is not null)
            {
                writer.WriteLine(feature);
            }
            writer.WriteLine(RustConstants.AttributeNoRustFmt);
            writer.WriteLine($"static {arrayName}: [&str; {enumValues.Length}] = [");
            using (writer.Indent()) {
                foreach (var value in enumValues)
                {
                    writer.WriteLine($"\"{value.Name(idConverter)}\",");
                }
            }
            writer.WriteLine("];");

            // #[derive(Debug)] isn't used since it generates a big switch statement. This code
            // uses a simple array lookup which has very little code. For small enums the default
            // implementation might be better though.
            if (feature is not null)
            {
                writer.WriteLine(feature);
            }
            writer.WriteLine($"impl fmt::Debug for {enumTypeName} {{");
            using (writer.Indent()) {
                writer.WriteLine(RustConstants.AttributeInline);
                writer.WriteLine($"fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {{");
                using (writer.Indent())
                    writer.WriteLine($"write!(f, \"{{}}\", {arrayName}[*self as usize])");
                writer.WriteLine("}");
            }
            writer.WriteLine("}");

            if (feature is not null)
            {
                writer.WriteLine(feature);
            }
            writer.WriteLine($"impl Default for {enumTypeName} {{");
            using (writer.Indent()) {
                writer.WriteLine(RustConstants.AttributeMustUse);
                writer.WriteLine(RustConstants.AttributeInline);
                writer.WriteLine("fn default() -> Self {");
                using (writer.Indent()) {
                    var defaultValue = enumValues[0];
                    // The first one should always have value 0 (eg. "None" field), and if the first one doesn't
                    // have value 0, there must be no other enum fields == 0.
                    if (defaultValue.Value != 0 && enumValues.Any(a => a.Value == 0))
                    {
                        throw new InvalidOperationException();
                    }
                    writer.WriteLine($"{enumTypeName}::{defaultValue.Name(idConverter)}");
                }
                writer.WriteLine("}");
            }
            writer.WriteLine("}");

            if (enumType.IsPublic)
            {
                var enumIterType       = $"{enumTypeName}Iterator";
                var icedConstValue     = "IcedConstants::" + idConverter.Constant(IcedConstants.GetEnumCountName(enumType.TypeId));
                var enumUnderlyingType = GetUnderlyingTypeStr(enumType);

                // Associated method: values()

                if (feature is not null)
                {
                    writer.WriteLine(feature);
                }
                writer.WriteLine(RustConstants.AttributeNoRustFmt);
                writer.WriteLine($"impl {enumTypeName} {{");
                using (writer.Indent()) {
                    writer.WriteLine($"/// Iterates over all `{enumTypeName}` enum values");
                    writer.WriteLine("#[inline]");
                    writer.WriteLine($"pub fn values() -> impl Iterator<Item = {enumTypeName}> + DoubleEndedIterator + ExactSizeIterator + FusedIterator {{");
                    using (writer.Indent()) {
                        if (enumType.Values.Length == 1)
                        {
                            writer.WriteLine($"static VALUES: [{enumTypeName}; 1] = [{enumTypeName}::{enumType.Values[0].Name(idConverter)}];");
                            writer.WriteLine($"VALUES.iter().copied()");
                        }
                        else
                        {
                            writer.WriteLine("// SAFETY: all values 0-max are valid enum values");
                            writer.WriteLine($"(0..{icedConstValue}).map(|x| unsafe {{ mem::transmute::<{enumUnderlyingType}, {enumTypeName}>(x as {enumUnderlyingType}) }})");
                        }
                    }
                    writer.WriteLine("}");
                }
                writer.WriteLine("}");
                writer.WriteLine("#[test]");
                if (feature is not null)
                {
                    writer.WriteLine(feature);
                }
                writer.WriteLine(RustConstants.AttributeNoRustFmt);
                writer.WriteLine($"fn test_{enumTypeName.ToLowerInvariant()}_values() {{");
                using (writer.Indent()) {
                    writer.WriteLine($"let mut iter = {enumTypeName}::values();");
                    writer.WriteLine($"assert_eq!(iter.size_hint(), ({icedConstValue}, Some({icedConstValue})));");
                    writer.WriteLine($"assert_eq!(iter.len(), {icedConstValue});");
                    writer.WriteLine("assert!(iter.next().is_some());");
                    writer.WriteLine($"assert_eq!(iter.size_hint(), ({icedConstValue} - 1, Some({icedConstValue} - 1)));");
                    writer.WriteLine($"assert_eq!(iter.len(), {icedConstValue} - 1);");
                    writer.WriteLine();
                    writer.WriteLine($"let values: Vec<{enumTypeName}> = {enumTypeName}::values().collect();");
                    writer.WriteLine($"assert_eq!(values.len(), {icedConstValue});");
                    writer.WriteLine("for (i, value) in values.into_iter().enumerate() {");
                    using (writer.Indent())
                        writer.WriteLine("assert_eq!(i, value as usize);");
                    writer.WriteLine("}");
                    writer.WriteLine();
                    writer.WriteLine($"let values1: Vec<{enumTypeName}> = {enumTypeName}::values().collect();");
                    writer.WriteLine($"let mut values2: Vec<{enumTypeName}> = {enumTypeName}::values().rev().collect();");
                    writer.WriteLine("values2.reverse();");
                    writer.WriteLine("assert_eq!(values1, values2);");
                }
                writer.WriteLine("}");

                // impl trait TryFrom

                var tryFromTypes = new string[] {
                    "usize",
                    //"u32",
                };
                foreach (var tryFromType in tryFromTypes)
                {
                    var castToFromType = tryFromType == "usize" ? string.Empty : $" as {tryFromType}";
                    if (feature is not null)
                    {
                        writer.WriteLine(feature);
                    }
                    writer.WriteLine(RustConstants.AttributeNoRustFmt);
                    writer.WriteLine($"impl TryFrom<{tryFromType}> for {enumTypeName} {{");
                    using (writer.Indent()) {
                        writer.WriteLine("type Error = IcedError;");
                        writer.WriteLine("#[inline]");
                        writer.WriteLine($"fn try_from(value: {tryFromType}) -> Result<Self, Self::Error> {{");
                        using (writer.Indent()) {
                            writer.WriteLine($"if value < {icedConstValue}{castToFromType} {{");
                            using (writer.Indent()) {
                                if (enumType.Values.Length == 1)
                                {
                                    writer.WriteLine($"Ok({enumTypeName}::{enumType.Values[0].Name(idConverter)})");
                                }
                                else
                                {
                                    writer.WriteLine("// SAFETY: all values 0-max are valid enum values");
                                    writer.WriteLine($"Ok(unsafe {{ mem::transmute(value as {enumUnderlyingType}) }})");
                                }
                            }
                            writer.WriteLine("} else {");
                            using (writer.Indent())
                                writer.WriteLine($"Err(IcedError::new(\"Invalid {enumTypeName} value\"))");
                            writer.WriteLine("}");
                        }
                        writer.WriteLine("}");
                    }
                    writer.WriteLine("}");
                    if (feature is not null)
                    {
                        writer.WriteLine(feature);
                    }
                    writer.WriteLine("#[test]");
                    writer.WriteLine(RustConstants.AttributeNoRustFmt);
                    writer.WriteLine($"fn test_{enumTypeName.ToLowerInvariant()}_try_from_{tryFromType}() {{");
                    using (writer.Indent()) {
                        writer.WriteLine($"for value in {enumTypeName}::values() {{");
                        using (writer.Indent()) {
                            writer.WriteLine($"let converted = <{enumTypeName} as TryFrom<{tryFromType}>>::try_from(value as {tryFromType}).unwrap();");
                            writer.WriteLine("assert_eq!(converted, value);");
                        }
                        writer.WriteLine("}");
                        writer.WriteLine($"assert!(<{enumTypeName} as TryFrom<{tryFromType}>>::try_from({icedConstValue}{castToFromType}).is_err());");
                        writer.WriteLine($"assert!(<{enumTypeName} as TryFrom<{tryFromType}>>::try_from(core::{tryFromType}::MAX).is_err());");
                    }
                    writer.WriteLine("}");
                }
            }
        }
示例#7
0
        public void Generate()
        {
            var          icedConstants = genTypes.GetConstantsType(TypeIds.IcedConstants);
            var          defs          = genTypes.GetObject <InstructionDefs>(TypeIds.InstructionDefs).Defs;
            const string ClassName     = "InstructionOpCounts";

            using (var writer = new FileWriter(TargetLanguage.CSharp, FileUtils.OpenWrite(CSharpConstants.GetFilename(genTypes, CSharpConstants.IcedNamespace, ClassName + ".g.cs")))) {
                writer.WriteFileHeader();

                writer.WriteLine($"namespace {CSharpConstants.IcedNamespace} {{");
                using (writer.Indent()) {
                    writer.WriteLine($"static class {ClassName} {{");
                    using (writer.Indent()) {
                        writer.WriteLineNoIndent($"#if {CSharpConstants.HasSpanDefine}");
                        writer.WriteLine($"internal static System.ReadOnlySpan<byte> OpCount => new byte[{icedConstants.Name(idConverter)}.{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)}] {{");
                        writer.WriteLineNoIndent("#else");
                        writer.WriteLine($"internal static readonly byte[] OpCount = new byte[{icedConstants.Name(idConverter)}.{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)}] {{");
                        writer.WriteLineNoIndent("#endif");
                        using (writer.Indent()) {
                            foreach (var def in defs)
                            {
                                writer.WriteLine($"{def.OpCount},// {def.Code.Name(idConverter)}");
                            }
                        }
                        writer.WriteLine("};");
                    }
                    writer.WriteLine("}");
                }
                writer.WriteLine("}");
            }
        }
示例#8
0
        protected void TestInstructionInfo(int bitness, string hexBytes, Code code, DecoderOptions options, int lineNo, InstructionInfoTestCase testCase)
        {
            var         codeBytes = HexUtils.ToByteArray(hexBytes);
            Instruction instruction;

            if (testCase.IsSpecial)
            {
                if (bitness == 16 && code == Code.Popw_CS && hexBytes == "0F")
                {
                    instruction             = default;
                    instruction.Code        = Code.Popw_CS;
                    instruction.Op0Kind     = OpKind.Register;
                    instruction.Op0Register = Register.CS;
                    instruction.CodeSize    = CodeSize.Code16;
                    instruction.Length      = 1;
                }
                else if (code <= Code.DeclareQword)
                {
                    instruction                  = default;
                    instruction.Code             = code;
                    instruction.DeclareDataCount = 1;
                    Assert.Equal(64, bitness);
                    instruction.CodeSize = CodeSize.Code64;
                    switch (code)
                    {
                    case Code.DeclareByte:
                        Assert.Equal("66", hexBytes);
                        instruction.SetDeclareByteValue(0, 0x66);
                        break;

                    case Code.DeclareWord:
                        Assert.Equal("6644", hexBytes);
                        instruction.SetDeclareWordValue(0, 0x4466);
                        break;

                    case Code.DeclareDword:
                        Assert.Equal("664422EE", hexBytes);
                        instruction.SetDeclareDwordValue(0, 0xEE224466);
                        break;

                    case Code.DeclareQword:
                        Assert.Equal("664422EE12345678", hexBytes);
                        instruction.SetDeclareQwordValue(0, 0x78563412EE224466);
                        break;

                    default: throw new InvalidOperationException();
                    }
                }
                else if (code == Code.Zero_bytes)
                {
                    instruction      = default;
                    instruction.Code = code;
                    Assert.Equal(64, bitness);
                    instruction.CodeSize = CodeSize.Code64;
                    Assert.Equal("", hexBytes);
                }
                else
                {
                    var decoder = CreateDecoder(bitness, codeBytes, testCase.IP, options);
                    instruction = decoder.Decode();
                    if (codeBytes.Length > 1 && codeBytes[0] == 0x9B && instruction.Length == 1)
                    {
                        instruction      = decoder.Decode();
                        instruction.Code = instruction.Code switch {
                            Code.Fnstenv_m14byte => Code.Fstenv_m14byte,
                            Code.Fnstenv_m28byte => Code.Fstenv_m28byte,
                            Code.Fnstcw_m2byte => Code.Fstcw_m2byte,
                            Code.Fneni => Code.Feni,
                            Code.Fndisi => Code.Fdisi,
                            Code.Fnclex => Code.Fclex,
                            Code.Fninit => Code.Finit,
                            Code.Fnsetpm => Code.Fsetpm,
                            Code.Fnsave_m94byte => Code.Fsave_m94byte,
                            Code.Fnsave_m108byte => Code.Fsave_m108byte,
                            Code.Fnstsw_m2byte => Code.Fstsw_m2byte,
                            Code.Fnstsw_AX => Code.Fstsw_AX,
                            Code.Fnstdw_AX => Code.Fstdw_AX,
                            Code.Fnstsg_AX => Code.Fstsg_AX,
                            _ => throw new InvalidOperationException(),
                        };
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }
                }
            }
            else
            {
                var decoder = CreateDecoder(bitness, codeBytes, testCase.IP, options);
                instruction = decoder.Decode();
            }
            Assert.Equal(code, instruction.Code);

            Assert.Equal(testCase.StackPointerIncrement, instruction.StackPointerIncrement);

            var info = new InstructionInfoFactory().GetInfo(instruction);

            Assert.Equal(testCase.Op0Access, info.Op0Access);
            Assert.Equal(testCase.Op1Access, info.Op1Access);
            Assert.Equal(testCase.Op2Access, info.Op2Access);
            Assert.Equal(testCase.Op3Access, info.Op3Access);
            Assert.Equal(testCase.Op4Access, info.Op4Access);
            var fpuInfo = instruction.GetFpuStackIncrementInfo();

            Assert.Equal(testCase.FpuTopIncrement, fpuInfo.Increment);
            Assert.Equal(testCase.FpuConditionalTop, fpuInfo.Conditional);
            Assert.Equal(testCase.FpuWritesTop, fpuInfo.WritesTop);
            Assert.Equal(
                new HashSet <UsedMemory>(testCase.UsedMemory, UsedMemoryEqualityComparer.Instance),
                new HashSet <UsedMemory>(info.GetUsedMemory(), UsedMemoryEqualityComparer.Instance));
            Assert.Equal(
                new HashSet <UsedRegister>(GetUsedRegisters(testCase.UsedRegisters), UsedRegisterEqualityComparer.Instance),
                new HashSet <UsedRegister>(GetUsedRegisters(info.GetUsedRegisters()), UsedRegisterEqualityComparer.Instance));

            Static.Assert(IcedConstants.MaxOpCount == 5 ? 0 : -1);
            Debug.Assert(instruction.OpCount <= IcedConstants.MaxOpCount);
            for (int i = 0; i < instruction.OpCount; i++)
            {
                switch (i)
                {
                case 0:
                    Assert.Equal(testCase.Op0Access, info.GetOpAccess(i));
                    break;

                case 1:
                    Assert.Equal(testCase.Op1Access, info.GetOpAccess(i));
                    break;

                case 2:
                    Assert.Equal(testCase.Op2Access, info.GetOpAccess(i));
                    break;

                case 3:
                    Assert.Equal(testCase.Op3Access, info.GetOpAccess(i));
                    break;

                case 4:
                    Assert.Equal(testCase.Op4Access, info.GetOpAccess(i));
                    break;

                default:
                    throw new InvalidOperationException();
                }
            }
            for (int i = instruction.OpCount; i < IcedConstants.MaxOpCount; i++)
            {
                Assert.Equal(OpAccess.None, info.GetOpAccess(i));
            }

            var info2 = new InstructionInfoFactory().GetInfo(instruction, InstructionInfoOptions.None);

            CheckEqual(ref info, ref info2, hasRegs2: true, hasMem2: true);
            info2 = new InstructionInfoFactory().GetInfo(instruction, InstructionInfoOptions.NoMemoryUsage);
            CheckEqual(ref info, ref info2, hasRegs2: true, hasMem2: false);
            info2 = new InstructionInfoFactory().GetInfo(instruction, InstructionInfoOptions.NoRegisterUsage);
            CheckEqual(ref info, ref info2, hasRegs2: false, hasMem2: true);
            info2 = new InstructionInfoFactory().GetInfo(instruction, InstructionInfoOptions.NoRegisterUsage | InstructionInfoOptions.NoMemoryUsage);
            CheckEqual(ref info, ref info2, hasRegs2: false, hasMem2: false);

            Assert.Equal(testCase.Encoding, instruction.Code.Encoding());
#if ENCODER && OPCODE_INFO
            Assert.Equal(code.ToOpCode().Encoding, testCase.Encoding);
#endif
            Assert.Equal(testCase.CpuidFeatures, instruction.Code.CpuidFeatures());
            Assert.Equal(testCase.FlowControl, instruction.Code.FlowControl());
            Assert.Equal(testCase.IsPrivileged, instruction.Code.IsPrivileged());
            Assert.Equal(testCase.IsStackInstruction, instruction.Code.IsStackInstruction());
            Assert.Equal(testCase.IsSaveRestoreInstruction, instruction.Code.IsSaveRestoreInstruction());

            Assert.Equal(testCase.Encoding, instruction.Encoding);
#if MVEX
            if (instruction.Encoding == EncodingKind.MVEX)
            {
                Assert.True(IcedConstants.IsMvex(instruction.Code));
            }
            else
            {
                Assert.False(IcedConstants.IsMvex(instruction.Code));
            }
#endif
            Assert.Equal(testCase.CpuidFeatures, instruction.CpuidFeatures);
            Assert.Equal(testCase.FlowControl, instruction.FlowControl);
            Assert.Equal(testCase.IsPrivileged, instruction.IsPrivileged);
            Assert.Equal(testCase.IsStackInstruction, instruction.IsStackInstruction);
            Assert.Equal(testCase.IsSaveRestoreInstruction, instruction.IsSaveRestoreInstruction);
            Assert.Equal(testCase.RflagsRead, instruction.RflagsRead);
            Assert.Equal(testCase.RflagsWritten, instruction.RflagsWritten);
            Assert.Equal(testCase.RflagsCleared, instruction.RflagsCleared);
            Assert.Equal(testCase.RflagsSet, instruction.RflagsSet);
            Assert.Equal(testCase.RflagsUndefined, instruction.RflagsUndefined);
            Assert.Equal(testCase.RflagsWritten | testCase.RflagsCleared | testCase.RflagsSet | testCase.RflagsUndefined, instruction.RflagsModified);

            Assert.Equal(RflagsBits.None, instruction.RflagsWritten & (instruction.RflagsCleared | instruction.RflagsSet | instruction.RflagsUndefined));
            Assert.Equal(RflagsBits.None, instruction.RflagsCleared & (instruction.RflagsWritten | instruction.RflagsSet | instruction.RflagsUndefined));
            Assert.Equal(RflagsBits.None, instruction.RflagsSet & (instruction.RflagsWritten | instruction.RflagsCleared | instruction.RflagsUndefined));
            Assert.Equal(RflagsBits.None, instruction.RflagsUndefined & (instruction.RflagsWritten | instruction.RflagsCleared | instruction.RflagsSet));
        }
        public void Generate()
        {
            var genTypes      = generatorContext.Types;
            var icedConstants = genTypes.GetConstantsType(TypeIds.IcedConstants);
            var defs          = genTypes.GetObject <InstructionDefs>(TypeIds.InstructionDefs).Defs;

            using (var writer = new FileWriter(TargetLanguage.Rust, FileUtils.OpenWrite(generatorContext.Types.Dirs.GetRustFilename("instruction_op_counts.rs")))) {
                writer.WriteFileHeader();
                writer.WriteLine($"use super::iced_constants::{icedConstants.Name(idConverter)};");
                writer.WriteLine();
                writer.WriteLine(RustConstants.AttributeNoRustFmt);
                writer.WriteLine($"pub(super) static OP_COUNT: [u8; {icedConstants.Name(idConverter)}::{icedConstants[IcedConstants.GetEnumCountName(TypeIds.Code)].Name(idConverter)}] = [");
                using (writer.Indent()) {
                    foreach (var def in defs)
                    {
                        writer.WriteLine($"{def.OpCount},// {def.Code.Name(idConverter)}");
                    }
                }
                writer.WriteLine("];");
            }
        }