Skip to content

Commit f2f422b

Browse files
committed
Feature: more work on x86 APX decoding.
1 parent 21b8c0d commit f2f422b

File tree

9 files changed

+66
-34
lines changed

9 files changed

+66
-34
lines changed

src/Arch/X86/Disassembler/X86Disassembler.Decoders.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ public Rex2Decoder(Decoder[] map0decoders, Decoder[] map1decoders)
179179

180180
public override X86Instruction Decode(uint rex2Prefix, X86Disassembler disasm)
181181
{
182+
if (disasm.decodingContext.RexPrefix)
183+
return disasm.CreateInvalidInstruction();
182184
if (!disasm.TryReadByte(out var rex2))
183185
return disasm.CreateInvalidInstruction();
184186
if (!disasm.TryReadByte(out var op))
@@ -521,13 +523,15 @@ public class EvexDecoder : Decoder
521523
private readonly Decoder[] decoders0F38;
522524
private readonly Decoder[] decoders0F3A;
523525
private readonly Decoder[] decodersLegacy;
526+
private readonly bool isApx;
524527

525528
private static readonly Bitfield p0Reserved = new Bitfield(2, 2);
526529
private static readonly Bitfield p1Reserved = new Bitfield(2, 1);
527530
private static readonly Bitfield p1Vvvv = new Bitfield(3, 4);
528531

529-
public EvexDecoder(Decoder[] legacy, Decoder[] decoders0F, Decoder[] decoders0F38, Decoder[] decoders0F3A)
532+
public EvexDecoder(bool isApx, Decoder[] legacy, Decoder[] decoders0F, Decoder[] decoders0F38, Decoder[] decoders0F3A)
530533
{
534+
this.isApx = isApx;
531535
this.decoders0F = decoders0F;
532536
this.decoders0F38 = decoders0F38;
533537
this.decoders0F3A = decoders0F3A;
@@ -536,7 +540,7 @@ public EvexDecoder(Decoder[] legacy, Decoder[] decoders0F, Decoder[] decoders0F3
536540

537541
public override X86Instruction Decode(uint op, X86Disassembler disasm)
538542
{
539-
// 62 must be the first byte. The presence of previous
543+
// 0x62 must be the first byte. The presence of previous
540544
// prefixes is an error (according to Intel manual 2.6, vol 2A).
541545
var ctx = disasm.decodingContext;
542546
if (ctx.F2Prefix |
@@ -546,26 +550,24 @@ public override X86Instruction Decode(uint op, X86Disassembler disasm)
546550
return disasm.CreateInvalidInstruction();
547551
// The EVEX prefix consists of a leading 0x62 byte, and three
548552
// packed payload bytes P0, P1, and P2.
549-
//$TODO: this is incomplete: there are many missing flags.
550553
if (!disasm.TryReadByte(out byte p0) ||
551554
!disasm.TryReadByte(out byte p1) ||
552555
!disasm.TryReadByte(out byte p2) ||
553-
p0Reserved.Read(p0) != 0 ||
554-
p1Reserved.Read(p1) != 1)
556+
(!isApx && p0Reserved.Read(p0) != 0) ||
557+
(!isApx && p1Reserved.Read(p1) != 1))
555558
{
556559
return disasm.CreateInvalidInstruction();
557560
}
558-
var mm = p0 & 3;
561+
var mmm = p0 & 7;
559562
var rxb = ~p0 >> 5;
560563
var pp = p1 & 3;
561564
var w = p1 >> 7;
562-
var vvvv = p1Vvvv.Read(~(uint)p1);
563-
if (!Bits.IsBitSet(p2, 3))
564-
vvvv |= 0x10;
565+
var vvvvv = p1Vvvv.Read(~(uint)p1);
566+
vvvvv |= (uint)(~p2 << 1) & 0x10;
565567
ctx.RexPrefix = true;
566568
ctx.IsVex = true;
567569
ctx.IsEvex = true;
568-
ctx.VexRegister = (byte) vvvv;
570+
ctx.VexRegister = (byte) vvvvv;
569571
ctx.VexLongCode = (byte)((p2 >> 5) & 3);
570572
ctx.OpMask = (byte)(p2 & 7);
571573
ctx.IsWide = w != 0;
@@ -581,16 +583,27 @@ public override X86Instruction Decode(uint op, X86Disassembler disasm)
581583
ctx.SizeOverridePrefix = pp == 1;
582584

583585
Decoder[] decoders;
584-
switch (mm)
586+
switch (mmm)
585587
{
586588
case 2: decoders = decoders0F38; break;
587589
case 3: decoders = decoders0F3A; break;
588-
case 4: decoders = decodersLegacy; break;
590+
case 4:
591+
decoders = decodersLegacy;
592+
ctx.EvexNF = Bits.IsBitSet(p2, 2);
593+
if (Bits.IsBitSet(p2, 4))
594+
{
595+
ctx.NewDataDestination = (int) vvvvv;
596+
}
597+
else if (vvvvv != 0)
598+
{
599+
return disasm.CreateInvalidInstruction();
600+
}
601+
break;
589602
default: decoders = decoders0F; break;
590603
}
591604
if (!disasm.TryReadByte(out byte op2))
592605
return disasm.CreateInvalidInstruction();
593-
TraceEvex(ctx, mm, op2);
606+
TraceEvex(ctx, mmm, op2);
594607
return decoders[op2].Decode(op2, disasm);
595608
}
596609
}

src/Arch/X86/Disassembler/X86Disassembler.OneByte.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ private void CreateOnebyteDecoders(
3737
{
3838
// 00
3939
d[0x00] = isEevex ? s_invalid : Instr(Mnemonic.add, InstrClass.Linear|InstrClass.Zero, Eb,Gb);
40-
d[0x01] = Instr(Mnemonic.add, Ndd,Ev,Gv);
40+
d[0x01] = Instr(Mnemonic.add, Nf, Ndd,Ev,Gv);
4141
d[0x02] = Instr(Mnemonic.add, Gb,Eb);
4242
d[0x03] = Instr(Mnemonic.add, Gv,Ev);
4343
d[0x04] = Instr(Mnemonic.add, AL,Ib);
@@ -181,6 +181,7 @@ private void CreateOnebyteDecoders(
181181
d[0x62] = isRex2 ? s_invalid : Amd64Instr(
182182
Instr186(Mnemonic.bound, Gv,Mv),
183183
new EvexDecoder(
184+
isEevex,
184185
eevexLegacy0,
185186
s_decoders0F,
186187
s_decoders0F38,

src/Arch/X86/Disassembler/X86Disassembler.cs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ internal void Reset()
9797
this.EvexMergeMode = 0;
9898
this.EvexBroadcast = false;
9999
this.NewDataDestination = null;
100+
this.NoFlags = false;
100101

101102
this.ops.Clear();
102103
}
@@ -196,9 +197,19 @@ internal byte ModRegMemByte
196197
public bool EvexBroadcast{ get; set; }
197198

198199
/// <summary>
199-
/// APX : New data destination register encoding
200+
/// APX: New data destination register encoding
200201
/// </summary>
201202
public int? NewDataDestination { get; set; }
203+
204+
/// <summary>
205+
/// APX: 'NF' no flags bit present in the EVEX prefix.
206+
/// </summary>
207+
public bool EvexNF { get; set; }
208+
209+
/// <summary>
210+
/// APX: True if setting the flags should be suppressed.
211+
/// </summary>
212+
public bool NoFlags { get; set; }
202213
}
203214

204215
//$REVIEW: Instructions longer than this cause exceptions on modern x86 processors.
@@ -599,7 +610,7 @@ private static Mutator<X86Disassembler> B(OperandType opType)
599610
private static readonly Mutator<X86Disassembler> By = B(OperandType.y);
600611

601612
/// <summary>
602-
/// Floating-point ST(x)
613+
/// Floating-point ST(x), extracted from the modrm field.
603614
/// </summary>
604615
private static bool F(uint op, X86Disassembler d)
605616
{
@@ -1386,6 +1397,20 @@ private static bool Ndd(uint op, X86Disassembler dasm)
13861397
return true;
13871398
}
13881399

1400+
/// <summary>
1401+
/// Sets the <see cref="X86Instruction.NoFlags"/> flag if the
1402+
/// instruction has an EVEX prefix with the 'no flags' bit set.
1403+
/// </summary>
1404+
private static bool Nf(uint op, X86Disassembler dasm)
1405+
{
1406+
var dc = dasm.decodingContext;
1407+
if (dc.EvexNF)
1408+
{
1409+
dc.NoFlags = true;
1410+
}
1411+
return true;
1412+
}
1413+
13891414
/// <summary>
13901415
/// Use the <see cref="NyiDecoder{TDasm, TMnemonic, TInstr}"/> to mark instructions for which no decoder has
13911416
/// been written yet.

src/Arch/X86/X86Instruction.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,18 @@ public class X86Instruction : MachineInstruction
5151
/// </summary>
5252
public int RepPrefix { get; set; }
5353

54-
[Obsolete("Use the `RepPrefix` property", true)]
55-
public int repPrefix; // 0 = no prefix, 2 = repnz, 3 = repz
56-
[Obsolete("Use the `DataWidth` property", true)]
57-
public DataType dataWidth = default!; // Width of the data (if it's a word).
58-
[Obsolete("Use the `AddressWidth` property", true)]
59-
public PrimitiveType addrWidth = default!; // width of the address mode. // TODO: belongs in MemoryOperand
60-
6154
public byte OpMask { get; set; } // EVEX Mask register to use.
6255
public byte MergingMode { get; set; } // EVEX merging mode
6356
public bool Broadcast { get; set; } // EVEX broadcast flag
6457
public EvexRoundMode RoundMode { get; set; }
6558
//$PERF: is it worth it to pack the rarely used bit- and byte-sized fields into a single word?
6659

67-
public X86Instruction(Mnemonic mnemonic, InstrClass iclass, DataType dataWidth, PrimitiveType addrWidth, params MachineOperand [] ops)
60+
/// <summary>
61+
/// True if instruction does not modify flags -- the Intel APX "NF" bit.
62+
/// </summary>
63+
public bool NoFlags { get; set; }
64+
65+
public X86Instruction(Mnemonic mnemonic, InstrClass iclass, DataType dataWidth, PrimitiveType addrWidth, params MachineOperand [] ops)
6866
{
6967
this.Mnemonic = mnemonic;
7068
this.InstructionClass = iclass;

src/Core/Machine/DisassemblerBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public bool MoveNext()
8383
var instr = dasm.DisassembleInstruction();
8484
if (instr is null || instr.Length < 0)
8585
return false;
86+
Debug.Assert(instr.Length > 0);
8687
this.current = instr;
8788
return true;
8889
}

src/Core/Machine/IRewriterHost.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ public interface IRewriterHost
5252
Expression? GetImport(Address addrThunk, Address addrInstr);
5353

5454
/// <summary>
55-
/// Given an address <paramref name="addrThunk"/>, returns the possible imported procedure
56-
///
55+
/// Given an address <paramref name="addrThunk"/>, returns the possible imported procedure.
5756
/// </summary>
5857
/// <param name="arch">Current <see cref="IProcessorArchitecture"/>.</param>
5958
/// <param name="addrThunk">Address of a thunk.</param>
@@ -72,7 +71,6 @@ public interface IRewriterHost
7271
/// <param name="arch">Current <see cref="IProcessorArchitecture"/>.</param>
7372
/// <param name="addrImportThunk"></param>
7473
/// <returns></returns>
75-
7674
ExternalProcedure? GetInterceptedCall(IProcessorArchitecture arch, Address addrImportThunk);
7775

7876
/// <summary>

src/UnitTests/Arch/X86/Disassembler/X86DisassemblerTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,8 +1933,6 @@ public void X86Dis_vmaxpd()
19331933
AssertCode64("vmaxpd\tymm6{k7},ymm5,qword ptr [rax]{1to4}", "62 F1 D5 3F 5F 30");
19341934
}
19351935

1936-
1937-
19381936
[Test]
19391937
public void X86Dis_vpminsq()
19401938
{

src/UnitTests/Arch/X86/Disassembler/X86Disassembler_apx_Tests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public void X86Dis_rex2_illegal_opcodes()
7171
AssertCode("illegal", "D508 40 03C4");
7272
AssertCode("illegal", "D508 72 45");
7373
AssertCode("illegal", "D508 A0 42");
74+
AssertCode("illegal", "40 D50803C4"); // REX prefix before REX2
7475
}
7576
}
7677

subjects/regression.log

Lines changed: 2 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)