diff --git a/src/Zydis.Generator.Core/Definitions/Builder/EncodingRegistry.cs b/src/Zydis.Generator.Core/Definitions/Builder/EncodingRegistry.cs index a8ccf9f..07a8d92 100644 --- a/src/Zydis.Generator.Core/Definitions/Builder/EncodingRegistry.cs +++ b/src/Zydis.Generator.Core/Definitions/Builder/EncodingRegistry.cs @@ -51,7 +51,7 @@ private static PhysicalInstructionEncoding GetPhysicalInstructionEncoding(Instru (definition.GetDecisionNodeIndex(ModrmRmNode.NodeDefinition.Instance) is not null); var hasIS4 = false; - PhysicalInstructionEncodingDisp? displacement = null; + SizeTable? displacement = null; List? immediates = null; foreach (var operand in definition.Operands ?? []) @@ -83,31 +83,31 @@ private static PhysicalInstructionEncoding GetPhysicalInstructionEncoding(Instru break; case OperandEncoding.Disp8: - displacement = new() { Width16 = 8, Width32 = 8, Width64 = 8 }; + displacement = new SizeTable(8, 8, 8); break; case OperandEncoding.Disp16: - displacement = new() { Width16 = 16, Width32 = 16, Width64 = 16 }; + displacement = new SizeTable(16, 16, 16); break; case OperandEncoding.Disp32: - displacement = new() { Width16 = 32, Width32 = 32, Width64 = 32 }; + displacement = new SizeTable(32, 32, 32); break; case OperandEncoding.Disp64: - displacement = new() { Width16 = 64, Width32 = 64, Width64 = 64 }; + displacement = new SizeTable(64, 64, 64); break; case OperandEncoding.Disp16_32_64: - displacement = new() { Width16 = 16, Width32 = 32, Width64 = 64 }; + displacement = new SizeTable(16, 32, 64); break; case OperandEncoding.Disp32_32_64: - displacement = new() { Width16 = 32, Width32 = 32, Width64 = 648 }; + displacement = new SizeTable(32, 32, 64); break; case OperandEncoding.Disp16_32_32: - displacement = new() { Width16 = 16, Width32 = 32, Width64 = 32 }; + displacement = new SizeTable(16, 32, 32); break; case OperandEncoding.Uimm8: @@ -226,7 +226,7 @@ public sealed class PhysicalInstructionEncoding : #pragma warning restore CA1036 { public bool HasModrm { get; init; } - public PhysicalInstructionEncodingDisp? Displacement { get; init; } + public SizeTable? Displacement { get; init; } public PhysicalInstructionEncodingImm? Immediate0 { get; init; } public PhysicalInstructionEncodingImm? Immediate1 { get; init; } public bool ForceRegForm { get; init; } @@ -279,61 +279,6 @@ public override int GetHashCode() #pragma warning disable CA1036 -public sealed class PhysicalInstructionEncodingDisp : - IComparable, - IComparable, - IEquatable - -#pragma warning restore CA1036 -{ - public int Width16 { get; init; } - public int Width32 { get; init; } - public int Width64 { get; init; } - - public int CompareTo(PhysicalInstructionEncodingDisp? other) - { - return FluentComparer.Compare(this, other, - x => x.Compare(x => x.Width16), - x => x.Compare(x => x.Width32), - x => x.Compare(x => x.Width64) - ); - } - - public int CompareTo(object? obj) - { - return CompareTo(obj as PhysicalInstructionEncodingDisp); - } - - public bool Equals(PhysicalInstructionEncodingDisp? other) - { - if (other is null) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return (Width16 == other.Width16) && - (Width32 == other.Width32) && - (Width64 == other.Width64); - } - - public override bool Equals(object? obj) - { - return ReferenceEquals(this, obj) || (obj is PhysicalInstructionEncodingDisp other) && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Width16, Width32, Width64); - } -} - -#pragma warning disable CA1036 - [Emittable(0, "size")] public sealed class PhysicalInstructionEncodingImm : IComparable, diff --git a/src/Zydis.Generator.Core/Definitions/Builder/OperandsRegistry.cs b/src/Zydis.Generator.Core/Definitions/Builder/OperandsRegistry.cs index e71a314..0565ba5 100644 --- a/src/Zydis.Generator.Core/Definitions/Builder/OperandsRegistry.cs +++ b/src/Zydis.Generator.Core/Definitions/Builder/OperandsRegistry.cs @@ -1,17 +1,27 @@ +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; + +using Zydis.Generator.Core.Helpers; +using Zydis.Generator.Enums; namespace Zydis.Generator.Core.Definitions.Builder; internal sealed class OperandsRegistry { private readonly List _operands = new(); - private readonly ConditionalWeakTable _lut = new(); + + private readonly SortedSet _sizes = new(); + + private readonly SortedSet _details = new(); public IReadOnlyList Operands => _operands; + public IReadOnlySet Sizes => _sizes; + + public IReadOnlySet OperandsDetails => _details; + public void Initialize(IEnumerable definitions) { foreach (var definition in definitions.OrderByDescending(x => x.NumberOfOperands)) @@ -47,6 +57,11 @@ private void InsertOperands(IReadOnlyList operands) } _operands.AddRange(operands); + foreach (var operand in operands) + { + _sizes.Add(GetSizeTable(operand)); + _details.Add(GetDetails(operand)); + } } private int FindOperandsIndex(IReadOnlyList operands) @@ -62,11 +77,68 @@ private int FindOperandsIndex(IReadOnlyList operands) if (operands.SequenceEqual(_operands.Skip(index).Take(operands.Count))) { - var test = _operands[index]; return index; } } return -1; } + + public static SizeTable GetSizeTable(InstructionOperand operand) + { + return new SizeTable(operand.Width16, operand.Width32, operand.Width64); + } + + public static OperandDetails GetDetails(InstructionOperand operand) + { + var type = "OTHER"; + if (operand.Type is OperandType.ImplicitReg) + { + type = operand.Register.GetRegisterClass() switch + { + RegisterClass.GPROSZ => "GPR_OSZ", + RegisterClass.GPRASZ => "GPR_ASZ", + RegisterClass.GPRSSZ => "GPR_SSZ", + _ => "STATIC" + }; + type = operand.Register switch + { + Register.ASZIP => "IP_ASZ", + Register.SSZIP => "IP_SSZ", + Register.SSZFLAGS => "FLAGS_SSZ", + _ => type + }; + } + else if (operand.Type is OperandType.ImplicitMem) + { + type = "MEMORY"; + } + + return new OperandDetails(type, operand.Encoding, operand.Register, operand.MemorySegment, operand.MemoryBase); + } +} + +#pragma warning disable CA1036 + +public sealed record OperandDetails(string Type, OperandEncoding Encoding, Register Register, SegmentRegister? MemorySegment = null, BaseRegister? MemoryBase = null) : + IComparable, + IComparable + +#pragma warning restore CA1036 +{ + public int CompareTo(OperandDetails? other) + { + return FluentComparer.Compare(this, other, + x => x.Compare(x => x.Type), + x => x.Compare(x => x.Encoding), + x => x.Compare(x => x.Register), + x => x.Compare(x => x.MemorySegment), + x => x.Compare(x => x.MemoryBase) + ); + } + + public int CompareTo(object? obj) + { + return CompareTo(obj as OperandDetails); + } } diff --git a/src/Zydis.Generator.Core/Definitions/Emitters/OperandsEmitter.cs b/src/Zydis.Generator.Core/Definitions/Emitters/OperandsEmitter.cs index fa62d0d..673aa54 100644 --- a/src/Zydis.Generator.Core/Definitions/Emitters/OperandsEmitter.cs +++ b/src/Zydis.Generator.Core/Definitions/Emitters/OperandsEmitter.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -27,75 +28,98 @@ public static async Task EmitAsync(StreamWriter writer, OperandsRegistry operand .WriteInitializerList() .BeginList(); var operandDeclaration = new ObjectDeclaration(); - var opDeclaration = new SimpleObjectDeclaration(InitializerType.Designated, "encoding", "reg", "mem"); + var sizes = operandsRegistry.Sizes.ToList(); + var details = operandsRegistry.OperandsDetails.ToList(); + Debug.Assert(sizes.Count < byte.MaxValue); + Debug.Assert(details.Count < byte.MaxValue); + + foreach (var operand in operandsRegistry.Operands) + { + var operandEntry = operandsWriter.CreateObjectWriter(operandDeclaration) + .WriteExpression("type", "ZYDIS_SEMANTIC_OPTYPE_{0}", operand.Type.ToZydisString()) + .WriteExpression("visibility", "ZYDIS_OPERAND_VISIBILITY_{0}", operand.Visibility.ToZydisString()) + .WriteExpression("actions", "ZYDIS_OPERAND_ACTION_{0}", operand.Access.ToZydisString()) + .WriteExpression("element_type", operand.ElementType.ToZydisString()) + .WriteBool("is_multisource4", operand.IsMultiSource4) + .WriteBool("ignore_seg_override", operand.IgnoreSegmentOverride) + .WriteInteger("size_reference", sizes.BinarySearch(OperandsRegistry.GetSizeTable(operand)), 2, true) + .WriteInteger("details_reference", details.BinarySearch(OperandsRegistry.GetDetails(operand)), 2, true); + + operandsWriter.WriteObject(operandEntry); + } + + operandsWriter.EndList(); + declarationWriter.EndDeclaration(); + declarationWriter.WriteNewline(); + declarationWriter.WriteNewline(); + + var sizesWriter = declarationWriter + .BeginDeclaration("static const", "ZyanU16", "OPERAND_SIZES[][3]") + .WriteInitializerList() + .BeginList(); + var sizeArrayDeclaration = new ArrayObjectDeclaration(3); + + foreach (var sizeTable in sizes) + { + var sizeTableEntry = new ObjectWriter(sizeArrayDeclaration, null) + .WriteInteger(0, sizeTable.Width16) + .WriteInteger(1, sizeTable.Width32) + .WriteInteger(2, sizeTable.Width64); + sizesWriter.WriteObject(sizeTableEntry); + } + + sizesWriter.EndList(); + declarationWriter.EndDeclaration(); + declarationWriter.WriteNewline(); + declarationWriter.WriteNewline(); + + var detailsWriter = declarationWriter + .BeginDeclaration("static const", "ZydisOperandDetails", "OPERAND_DETAILS[]") + .WriteInitializerList() + .BeginList(); + var detailsDeclaration = new SimpleObjectDeclaration(InitializerType.Designated, "encoding", "reg", "mem"); var regOuterDeclaration = new SimpleObjectDeclaration("type", "reg"); var regInnerDeclaration = new SimpleObjectDeclaration(InitializerType.Designated, "reg", "id"); var memDeclaration = new SimpleObjectDeclaration("seg", "base"); - foreach (var operand in operandsRegistry.Operands) + foreach (var opDetails in details) { - var operandEntry = operandsWriter.CreateObjectWriter(operandDeclaration); - var opEntry = operandEntry.CreateObjectWriter(opDeclaration); - if (operand.Type is OperandType.ImplicitReg) + var detailsEntry = detailsWriter.CreateObjectWriter(detailsDeclaration); + if (opDetails.Type == "MEMORY") + { + var memEntry = detailsEntry.CreateObjectWriter(memDeclaration); + memEntry + .WriteInteger("seg", (int)(opDetails.MemorySegment ?? SegmentRegister.None)) + .WriteExpression("base", opDetails.MemoryBase!.Value.ToZydisString()); + detailsEntry.WriteObject("mem", memEntry); + } + else if (opDetails.Type == "OTHER") + { + detailsEntry.WriteExpression("encoding", "ZYDIS_OPERAND_ENCODING_{0}", opDetails.Encoding.ToZydisString()); + } + else { - var regOuterEntry = opEntry.CreateObjectWriter(regOuterDeclaration); + var regOuterEntry = detailsEntry.CreateObjectWriter(regOuterDeclaration); var regInnerEntry = regOuterEntry.CreateObjectWriter(regInnerDeclaration); - var type = operand.Register.GetRegisterClass() switch - { - RegisterClass.GPROSZ => "GPR_OSZ", - RegisterClass.GPRASZ => "GPR_ASZ", - RegisterClass.GPRSSZ => "GPR_SSZ", - _ => "STATIC" - }; - type = operand.Register switch - { - Register.ASZIP => "IP_ASZ", - Register.SSZIP => "IP_SSZ", - Register.SSZFLAGS => "FLAGS_SSZ", - _ => type - }; - if (type == "STATIC") + if (opDetails.Type == "STATIC") { - regInnerEntry.WriteExpression("reg", "ZYDIS_REGISTER_{0}", operand.Register.ToZydisString()); + regInnerEntry.WriteExpression("reg", "ZYDIS_REGISTER_{0}", opDetails.Register.ToZydisString()); regOuterEntry.WriteExpression("type", "ZYDIS_IMPLREG_TYPE_STATIC"); - } else { - regInnerEntry.WriteInteger("id", operand.Register.GetRegisterId() & 0x3F, 4, true); - regOuterEntry.WriteExpression("type", "ZYDIS_IMPLREG_TYPE_{0}", type); + regInnerEntry.WriteInteger("id", opDetails.Register.GetRegisterId() & 0x3F, 4, true); + regOuterEntry.WriteExpression("type", "ZYDIS_IMPLREG_TYPE_{0}", opDetails.Type); } regOuterEntry.WriteObject("reg", regInnerEntry); - opEntry.WriteObject("reg", regOuterEntry); - } - else if (operand.Type is OperandType.ImplicitMem) - { - var memEntry = opEntry.CreateObjectWriter(memDeclaration); - memEntry - .WriteInteger("seg", (int)(operand.MemorySegment ?? SegmentRegister.None)) - .WriteExpression("base", operand.MemoryBase!.Value.ToZydisString()); - opEntry.WriteObject("mem", memEntry); - } - else - { - opEntry.WriteExpression("encoding", "ZYDIS_OPERAND_ENCODING_{0}", operand.Encoding.ToZydisString()); + detailsEntry.WriteObject("reg", regOuterEntry); } - operandEntry - .WriteExpression("type", "ZYDIS_SEMANTIC_OPTYPE_{0}", operand.Type.ToZydisString()) - .WriteExpression("visibility", "ZYDIS_OPERAND_VISIBILITY_{0}", operand.Visibility.ToZydisString()) - .WriteExpression("actions", "ZYDIS_OPERAND_ACTION_{0}", operand.Access.ToZydisString()) - .WriteIntegerArray("size", operand.Width16, operand.Width32, operand.Width64) - .WriteExpression("element_type", operand.ElementType.ToZydisString()) - .WriteObject("op", opEntry) - .WriteBool("is_multisource4", operand.IsMultiSource4) - .WriteBool("ignore_seg_override", operand.IgnoreSegmentOverride); - - operandsWriter.WriteObject(operandEntry); + detailsWriter.WriteObject(detailsEntry); } - operandsWriter.EndList(); + detailsWriter.EndList(); declarationWriter.EndDeclaration(); await writer.WriteLineAsync().ConfigureAwait(false); diff --git a/src/Zydis.Generator.Core/Definitions/InstructionOperand.cs b/src/Zydis.Generator.Core/Definitions/InstructionOperand.cs index 4d8c2cf..4596cb3 100644 --- a/src/Zydis.Generator.Core/Definitions/InstructionOperand.cs +++ b/src/Zydis.Generator.Core/Definitions/InstructionOperand.cs @@ -11,8 +11,8 @@ namespace Zydis.Generator.Core.Definitions; #pragma warning disable CA1036 -[Emittable(3, "size")] -[Emittable(5, "op")] +[Emittable(6, "size_reference")] +[Emittable(7, "details_reference")] public sealed class InstructionOperand : IComparable, IComparable, @@ -30,7 +30,7 @@ public sealed class InstructionOperand : public OperandEncoding Encoding { get; init; } - [Emittable(4)] + [Emittable(3)] public ElementType ElementType { get; init; } public ScaleFactor ScaleFactor { get; init; } @@ -42,11 +42,11 @@ public sealed class InstructionOperand : [JsonPropertyName("visible")] public bool? IsVisible { get; init; } - [Emittable(6, "is_multisource4")] + [Emittable(4, "is_multisource4")] [JsonPropertyName("is_multisource4")] public bool IsMultiSource4 { get; init; } - [Emittable(7, "ignore_seg_override")] + [Emittable(5, "ignore_seg_override")] [JsonPropertyName("ignore_seg_override")] public bool IgnoreSegmentOverride { get; init; } diff --git a/src/Zydis.Generator.Core/Definitions/SizeTable.cs b/src/Zydis.Generator.Core/Definitions/SizeTable.cs new file mode 100644 index 0000000..b13f3e8 --- /dev/null +++ b/src/Zydis.Generator.Core/Definitions/SizeTable.cs @@ -0,0 +1,28 @@ +using System; + +using Zydis.Generator.Core.Helpers; + +namespace Zydis.Generator.Core.Definitions; + +#pragma warning disable CA1036 + +public sealed record SizeTable(int Width16, int Width32, int Width64) : + IComparable, + IComparable + +#pragma warning restore CA1036 +{ + public int CompareTo(SizeTable? other) + { + return FluentComparer.Compare(this, other, + x => x.Compare(x => x.Width16), + x => x.Compare(x => x.Width32), + x => x.Compare(x => x.Width64) + ); + } + + public int CompareTo(object? obj) + { + return CompareTo(obj as SizeTable); + } +}