Skip to content

Commit 56722ae

Browse files
committed
Optimized operand definitions
1 parent df7bbef commit 56722ae

File tree

5 files changed

+191
-122
lines changed

5 files changed

+191
-122
lines changed

src/Zydis.Generator.Core/Definitions/Builder/EncodingRegistry.cs

Lines changed: 9 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ private static PhysicalInstructionEncoding GetPhysicalInstructionEncoding(Instru
5151
(definition.GetDecisionNodeIndex(ModrmRmNode.NodeDefinition.Instance) is not null);
5252

5353
var hasIS4 = false;
54-
PhysicalInstructionEncodingDisp? displacement = null;
54+
SizeTable? displacement = null;
5555
List<PhysicalInstructionEncodingImm>? immediates = null;
5656

5757
foreach (var operand in definition.Operands ?? [])
@@ -83,31 +83,31 @@ private static PhysicalInstructionEncoding GetPhysicalInstructionEncoding(Instru
8383
break;
8484

8585
case OperandEncoding.Disp8:
86-
displacement = new() { Width16 = 8, Width32 = 8, Width64 = 8 };
86+
displacement = new SizeTable(8, 8, 8);
8787
break;
8888

8989
case OperandEncoding.Disp16:
90-
displacement = new() { Width16 = 16, Width32 = 16, Width64 = 16 };
90+
displacement = new SizeTable(16, 16, 16);
9191
break;
9292

9393
case OperandEncoding.Disp32:
94-
displacement = new() { Width16 = 32, Width32 = 32, Width64 = 32 };
94+
displacement = new SizeTable(32, 32, 32);
9595
break;
9696

9797
case OperandEncoding.Disp64:
98-
displacement = new() { Width16 = 64, Width32 = 64, Width64 = 64 };
98+
displacement = new SizeTable(64, 64, 64);
9999
break;
100100

101101
case OperandEncoding.Disp16_32_64:
102-
displacement = new() { Width16 = 16, Width32 = 32, Width64 = 64 };
102+
displacement = new SizeTable(16, 32, 64);
103103
break;
104104

105105
case OperandEncoding.Disp32_32_64:
106-
displacement = new() { Width16 = 32, Width32 = 32, Width64 = 648 };
106+
displacement = new SizeTable(32, 32, 64);
107107
break;
108108

109109
case OperandEncoding.Disp16_32_32:
110-
displacement = new() { Width16 = 16, Width32 = 32, Width64 = 32 };
110+
displacement = new SizeTable(16, 32, 32);
111111
break;
112112

113113
case OperandEncoding.Uimm8:
@@ -226,7 +226,7 @@ public sealed class PhysicalInstructionEncoding :
226226
#pragma warning restore CA1036
227227
{
228228
public bool HasModrm { get; init; }
229-
public PhysicalInstructionEncodingDisp? Displacement { get; init; }
229+
public SizeTable? Displacement { get; init; }
230230
public PhysicalInstructionEncodingImm? Immediate0 { get; init; }
231231
public PhysicalInstructionEncodingImm? Immediate1 { get; init; }
232232
public bool ForceRegForm { get; init; }
@@ -279,61 +279,6 @@ public override int GetHashCode()
279279

280280
#pragma warning disable CA1036
281281

282-
public sealed class PhysicalInstructionEncodingDisp :
283-
IComparable<PhysicalInstructionEncodingDisp>,
284-
IComparable,
285-
IEquatable<PhysicalInstructionEncodingDisp>
286-
287-
#pragma warning restore CA1036
288-
{
289-
public int Width16 { get; init; }
290-
public int Width32 { get; init; }
291-
public int Width64 { get; init; }
292-
293-
public int CompareTo(PhysicalInstructionEncodingDisp? other)
294-
{
295-
return FluentComparer.Compare(this, other,
296-
x => x.Compare(x => x.Width16),
297-
x => x.Compare(x => x.Width32),
298-
x => x.Compare(x => x.Width64)
299-
);
300-
}
301-
302-
public int CompareTo(object? obj)
303-
{
304-
return CompareTo(obj as PhysicalInstructionEncodingDisp);
305-
}
306-
307-
public bool Equals(PhysicalInstructionEncodingDisp? other)
308-
{
309-
if (other is null)
310-
{
311-
return false;
312-
}
313-
314-
if (ReferenceEquals(this, other))
315-
{
316-
return true;
317-
}
318-
319-
return (Width16 == other.Width16) &&
320-
(Width32 == other.Width32) &&
321-
(Width64 == other.Width64);
322-
}
323-
324-
public override bool Equals(object? obj)
325-
{
326-
return ReferenceEquals(this, obj) || (obj is PhysicalInstructionEncodingDisp other) && Equals(other);
327-
}
328-
329-
public override int GetHashCode()
330-
{
331-
return HashCode.Combine(Width16, Width32, Width64);
332-
}
333-
}
334-
335-
#pragma warning disable CA1036
336-
337282
[Emittable(0, "size")]
338283
public sealed class PhysicalInstructionEncodingImm :
339284
IComparable<PhysicalInstructionEncodingImm>,

src/Zydis.Generator.Core/Definitions/Builder/OperandsRegistry.cs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.Linq;
4-
using System.Runtime.CompilerServices;
5+
6+
using Zydis.Generator.Core.Helpers;
7+
using Zydis.Generator.Enums;
58

69
namespace Zydis.Generator.Core.Definitions.Builder;
710

811
internal sealed class OperandsRegistry
912
{
1013
private readonly List<InstructionOperand> _operands = new();
11-
private readonly ConditionalWeakTable<InstructionDefinition, PhysicalInstructionEncoding> _lut = new();
14+
15+
private readonly SortedSet<SizeTable> _sizes = new();
16+
17+
private readonly SortedSet<OperandDetails> _details = new();
1218

1319
public IReadOnlyList<InstructionOperand> Operands => _operands;
1420

21+
public IReadOnlySet<SizeTable> Sizes => _sizes;
22+
23+
public IReadOnlySet<OperandDetails> OperandsDetails => _details;
24+
1525
public void Initialize(IEnumerable<InstructionDefinition> definitions)
1626
{
1727
foreach (var definition in definitions.OrderByDescending(x => x.NumberOfOperands))
@@ -47,6 +57,11 @@ private void InsertOperands(IReadOnlyList<InstructionOperand> operands)
4757
}
4858

4959
_operands.AddRange(operands);
60+
foreach (var operand in operands)
61+
{
62+
_sizes.Add(GetSizeTable(operand));
63+
_details.Add(GetDetails(operand));
64+
}
5065
}
5166

5267
private int FindOperandsIndex(IReadOnlyList<InstructionOperand> operands)
@@ -62,11 +77,68 @@ private int FindOperandsIndex(IReadOnlyList<InstructionOperand> operands)
6277

6378
if (operands.SequenceEqual(_operands.Skip(index).Take(operands.Count)))
6479
{
65-
var test = _operands[index];
6680
return index;
6781
}
6882
}
6983

7084
return -1;
7185
}
86+
87+
public static SizeTable GetSizeTable(InstructionOperand operand)
88+
{
89+
return new SizeTable(operand.Width16, operand.Width32, operand.Width64);
90+
}
91+
92+
public static OperandDetails GetDetails(InstructionOperand operand)
93+
{
94+
var type = "OTHER";
95+
if (operand.Type is OperandType.ImplicitReg)
96+
{
97+
type = operand.Register.GetRegisterClass() switch
98+
{
99+
RegisterClass.GPROSZ => "GPR_OSZ",
100+
RegisterClass.GPRASZ => "GPR_ASZ",
101+
RegisterClass.GPRSSZ => "GPR_SSZ",
102+
_ => "STATIC"
103+
};
104+
type = operand.Register switch
105+
{
106+
Register.ASZIP => "IP_ASZ",
107+
Register.SSZIP => "IP_SSZ",
108+
Register.SSZFLAGS => "FLAGS_SSZ",
109+
_ => type
110+
};
111+
}
112+
else if (operand.Type is OperandType.ImplicitMem)
113+
{
114+
type = "MEMORY";
115+
}
116+
117+
return new OperandDetails(type, operand.Encoding, operand.Register, operand.MemorySegment, operand.MemoryBase);
118+
}
119+
}
120+
121+
#pragma warning disable CA1036
122+
123+
public sealed record OperandDetails(string Type, OperandEncoding Encoding, Register Register, SegmentRegister? MemorySegment = null, BaseRegister? MemoryBase = null) :
124+
IComparable<OperandDetails>,
125+
IComparable
126+
127+
#pragma warning restore CA1036
128+
{
129+
public int CompareTo(OperandDetails? other)
130+
{
131+
return FluentComparer.Compare(this, other,
132+
x => x.Compare(x => x.Type),
133+
x => x.Compare(x => x.Encoding),
134+
x => x.Compare(x => x.Register),
135+
x => x.Compare(x => x.MemorySegment),
136+
x => x.Compare(x => x.MemoryBase)
137+
);
138+
}
139+
140+
public int CompareTo(object? obj)
141+
{
142+
return CompareTo(obj as OperandDetails);
143+
}
72144
}

src/Zydis.Generator.Core/Definitions/Emitters/OperandsEmitter.cs

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using System.IO;
4+
using System.Linq;
45
using System.Threading;
56
using System.Threading.Tasks;
67

@@ -27,75 +28,98 @@ public static async Task EmitAsync(StreamWriter writer, OperandsRegistry operand
2728
.WriteInitializerList()
2829
.BeginList();
2930
var operandDeclaration = new ObjectDeclaration<InstructionOperand>();
30-
var opDeclaration = new SimpleObjectDeclaration(InitializerType.Designated, "encoding", "reg", "mem");
31+
var sizes = operandsRegistry.Sizes.ToList();
32+
var details = operandsRegistry.OperandsDetails.ToList();
33+
Debug.Assert(sizes.Count < byte.MaxValue);
34+
Debug.Assert(details.Count < byte.MaxValue);
35+
36+
foreach (var operand in operandsRegistry.Operands)
37+
{
38+
var operandEntry = operandsWriter.CreateObjectWriter(operandDeclaration)
39+
.WriteExpression("type", "ZYDIS_SEMANTIC_OPTYPE_{0}", operand.Type.ToZydisString())
40+
.WriteExpression("visibility", "ZYDIS_OPERAND_VISIBILITY_{0}", operand.Visibility.ToZydisString())
41+
.WriteExpression("actions", "ZYDIS_OPERAND_ACTION_{0}", operand.Access.ToZydisString())
42+
.WriteExpression("element_type", operand.ElementType.ToZydisString())
43+
.WriteBool("is_multisource4", operand.IsMultiSource4)
44+
.WriteBool("ignore_seg_override", operand.IgnoreSegmentOverride)
45+
.WriteInteger("size_reference", sizes.BinarySearch(OperandsRegistry.GetSizeTable(operand)), 2, true)
46+
.WriteInteger("details_reference", details.BinarySearch(OperandsRegistry.GetDetails(operand)), 2, true);
47+
48+
operandsWriter.WriteObject(operandEntry);
49+
}
50+
51+
operandsWriter.EndList();
52+
declarationWriter.EndDeclaration();
53+
declarationWriter.WriteNewline();
54+
declarationWriter.WriteNewline();
55+
56+
var sizesWriter = declarationWriter
57+
.BeginDeclaration("static const", "ZyanU16", "OPERAND_SIZES[][3]")
58+
.WriteInitializerList()
59+
.BeginList();
60+
var sizeArrayDeclaration = new ArrayObjectDeclaration(3);
61+
62+
foreach (var sizeTable in sizes)
63+
{
64+
var sizeTableEntry = new ObjectWriter(sizeArrayDeclaration, null)
65+
.WriteInteger(0, sizeTable.Width16)
66+
.WriteInteger(1, sizeTable.Width32)
67+
.WriteInteger(2, sizeTable.Width64);
68+
sizesWriter.WriteObject(sizeTableEntry);
69+
}
70+
71+
sizesWriter.EndList();
72+
declarationWriter.EndDeclaration();
73+
declarationWriter.WriteNewline();
74+
declarationWriter.WriteNewline();
75+
76+
var detailsWriter = declarationWriter
77+
.BeginDeclaration("static const", "ZydisOperandDetails", "OPERAND_DETAILS[]")
78+
.WriteInitializerList()
79+
.BeginList();
80+
var detailsDeclaration = new SimpleObjectDeclaration(InitializerType.Designated, "encoding", "reg", "mem");
3181
var regOuterDeclaration = new SimpleObjectDeclaration("type", "reg");
3282
var regInnerDeclaration = new SimpleObjectDeclaration(InitializerType.Designated, "reg", "id");
3383
var memDeclaration = new SimpleObjectDeclaration("seg", "base");
3484

35-
foreach (var operand in operandsRegistry.Operands)
85+
foreach (var opDetails in details)
3686
{
37-
var operandEntry = operandsWriter.CreateObjectWriter(operandDeclaration);
38-
var opEntry = operandEntry.CreateObjectWriter(opDeclaration);
39-
if (operand.Type is OperandType.ImplicitReg)
87+
var detailsEntry = detailsWriter.CreateObjectWriter(detailsDeclaration);
88+
if (opDetails.Type == "MEMORY")
89+
{
90+
var memEntry = detailsEntry.CreateObjectWriter(memDeclaration);
91+
memEntry
92+
.WriteInteger("seg", (int)(opDetails.MemorySegment ?? SegmentRegister.None))
93+
.WriteExpression("base", opDetails.MemoryBase!.Value.ToZydisString());
94+
detailsEntry.WriteObject("mem", memEntry);
95+
}
96+
else if (opDetails.Type == "OTHER")
97+
{
98+
detailsEntry.WriteExpression("encoding", "ZYDIS_OPERAND_ENCODING_{0}", opDetails.Encoding.ToZydisString());
99+
}
100+
else
40101
{
41-
var regOuterEntry = opEntry.CreateObjectWriter(regOuterDeclaration);
102+
var regOuterEntry = detailsEntry.CreateObjectWriter(regOuterDeclaration);
42103
var regInnerEntry = regOuterEntry.CreateObjectWriter(regInnerDeclaration);
43-
var type = operand.Register.GetRegisterClass() switch
44-
{
45-
RegisterClass.GPROSZ => "GPR_OSZ",
46-
RegisterClass.GPRASZ => "GPR_ASZ",
47-
RegisterClass.GPRSSZ => "GPR_SSZ",
48-
_ => "STATIC"
49-
};
50-
type = operand.Register switch
51-
{
52-
Register.ASZIP => "IP_ASZ",
53-
Register.SSZIP => "IP_SSZ",
54-
Register.SSZFLAGS => "FLAGS_SSZ",
55-
_ => type
56-
};
57104

58-
if (type == "STATIC")
105+
if (opDetails.Type == "STATIC")
59106
{
60-
regInnerEntry.WriteExpression("reg", "ZYDIS_REGISTER_{0}", operand.Register.ToZydisString());
107+
regInnerEntry.WriteExpression("reg", "ZYDIS_REGISTER_{0}", opDetails.Register.ToZydisString());
61108
regOuterEntry.WriteExpression("type", "ZYDIS_IMPLREG_TYPE_STATIC");
62-
63109
}
64110
else
65111
{
66-
regInnerEntry.WriteInteger("id", operand.Register.GetRegisterId() & 0x3F, 4, true);
67-
regOuterEntry.WriteExpression("type", "ZYDIS_IMPLREG_TYPE_{0}", type);
112+
regInnerEntry.WriteInteger("id", opDetails.Register.GetRegisterId() & 0x3F, 4, true);
113+
regOuterEntry.WriteExpression("type", "ZYDIS_IMPLREG_TYPE_{0}", opDetails.Type);
68114
}
69115

70116
regOuterEntry.WriteObject("reg", regInnerEntry);
71-
opEntry.WriteObject("reg", regOuterEntry);
72-
}
73-
else if (operand.Type is OperandType.ImplicitMem)
74-
{
75-
var memEntry = opEntry.CreateObjectWriter(memDeclaration);
76-
memEntry
77-
.WriteInteger("seg", (int)(operand.MemorySegment ?? SegmentRegister.None))
78-
.WriteExpression("base", operand.MemoryBase!.Value.ToZydisString());
79-
opEntry.WriteObject("mem", memEntry);
80-
}
81-
else
82-
{
83-
opEntry.WriteExpression("encoding", "ZYDIS_OPERAND_ENCODING_{0}", operand.Encoding.ToZydisString());
117+
detailsEntry.WriteObject("reg", regOuterEntry);
84118
}
85-
operandEntry
86-
.WriteExpression("type", "ZYDIS_SEMANTIC_OPTYPE_{0}", operand.Type.ToZydisString())
87-
.WriteExpression("visibility", "ZYDIS_OPERAND_VISIBILITY_{0}", operand.Visibility.ToZydisString())
88-
.WriteExpression("actions", "ZYDIS_OPERAND_ACTION_{0}", operand.Access.ToZydisString())
89-
.WriteIntegerArray("size", operand.Width16, operand.Width32, operand.Width64)
90-
.WriteExpression("element_type", operand.ElementType.ToZydisString())
91-
.WriteObject("op", opEntry)
92-
.WriteBool("is_multisource4", operand.IsMultiSource4)
93-
.WriteBool("ignore_seg_override", operand.IgnoreSegmentOverride);
94-
95-
operandsWriter.WriteObject(operandEntry);
119+
detailsWriter.WriteObject(detailsEntry);
96120
}
97121

98-
operandsWriter.EndList();
122+
detailsWriter.EndList();
99123
declarationWriter.EndDeclaration();
100124

101125
await writer.WriteLineAsync().ConfigureAwait(false);

0 commit comments

Comments
 (0)