Skip to content

Commit 814e261

Browse files
committed
Improve registers. Improve decoding performance by inlining the selector code
1 parent 0b33fa2 commit 814e261

19 files changed

+2640
-2110
lines changed

src/AsmArm64.CodeGen/Arm64Processor.Writer.cs

Lines changed: 108 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ private void GenerateCode()
3535
GenerateProcessStateField();
3636
GenerateRegisterIndex();
3737
}
38-
3938

4039
private void GenerateMnemonicEnum()
4140
{
@@ -557,7 +556,8 @@ Action<CodeWriter, EncodingSymbolSelector, EncodingBitValue> writeSelector
557556
}
558557
else
559558
{
560-
w.WriteLine($"return TryDecodeFromBitValues(bitValue, {extract.SelectorIndex}, {string.Join(",", parameters.Select(x => $"out {x.ParameterName}"))});");
559+
WriteSelectorCode(w, extract.Selector!, parameters, writeSelector);
560+
//w.WriteLine($"return TryDecodeFromBitValues(bitValue, {extract.SelectorIndex}, {string.Join(",", parameters.Select(x => $"out {x.ParameterName}"))});");
561561
}
562562
}
563563
else
@@ -602,122 +602,127 @@ Action<CodeWriter, EncodingSymbolSelector, EncodingBitValue> writeSelector
602602
// return false;
603603
// }
604604

605-
if (map.SelectorList.Count > 0)
605+
//if (map.SelectorList.Count > 0)
606+
//{
607+
// w.WriteLine();
608+
// // We inline aggressively because the selectorIndex is a constant passed as it seems that the vast majority of selectorIndex are unique to an extract.
609+
// w.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
610+
// w.WriteLine($"public static bool TryDecodeFromBitValues(uint bitValue, byte selectorIndex, {parameterSignature})");
611+
// w.OpenBraceBlock();
612+
// {
613+
// w.WriteLine("switch (selectorIndex)");
614+
// w.OpenBraceBlock();
615+
// {
616+
// foreach (var selector in map.SelectorList)
617+
// {
618+
// w.WriteLine($"case {selector.Index}:");
619+
// w.OpenBraceBlock();
620+
// WriteSelectorCode(w, selector, parameters, writeSelector);
621+
622+
// w.CloseBraceBlock();
623+
// }
624+
// }
625+
// w.CloseBraceBlock();
626+
// w.WriteLine();
627+
// foreach (var parameter in parameters)
628+
// {
629+
// w.WriteLine($"{parameter.ParameterName} = default;");
630+
// }
631+
632+
// w.WriteLine("return false;");
633+
// }
634+
// w.CloseBraceBlock();
635+
//}
636+
}
637+
w.CloseBraceBlock();
638+
}
639+
640+
private void WriteSelectorCode(CodeWriter w, EncodingSymbolSelector selector, List<(string ParameterType, string ParameterName)> parameters, Action<CodeWriter, EncodingSymbolSelector, EncodingBitValue> writeSelector)
641+
{
642+
w.WriteLine(ExtractBitRangeString(selector.BitRanges, "bitValue", "bitsToTest"));
643+
644+
if (selector.Kind == EncodingSymbolSelectorKind.Regular)
645+
{
646+
w.WriteLine($"switch (bitsToTest)");
647+
w.OpenBraceBlock();
606648
{
607-
w.WriteLine();
608-
// We inline aggressively because the selectorIndex is a constant passed as it seems that the vast majority of selectorIndex are unique to an extract.
609-
w.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
610-
w.WriteLine($"public static bool TryDecodeFromBitValues(uint bitValue, byte selectorIndex, {parameterSignature})");
611-
w.OpenBraceBlock();
649+
foreach (var bitValue in selector.BitValues)
612650
{
613-
w.WriteLine("switch (selectorIndex)");
651+
w.WriteLine($"case {bitValue.BitSelectorValue}:");
614652
w.OpenBraceBlock();
653+
if (bitValue.Kind == EncodingBitValueKind.BitExtract)
615654
{
616-
foreach (var selector in map.SelectorList)
655+
var bitRanges = bitValue.BitItems.Select(x => x.Range).ToList();
656+
var bitValueExtractString = ExtractBitRangeString(bitRanges, "bitValue", "extractedValue");
657+
Debug.Assert(parameters.Count == 1);
658+
w.WriteLine(bitValueExtractString);
659+
660+
if (bitValue.Addend != 0)
617661
{
618-
w.WriteLine($"case {selector.Index}:");
619-
w.OpenBraceBlock();
620-
w.WriteLine(ExtractBitRangeString(selector.BitRanges, "bitValue", "bitsToTest"));
662+
w.WriteLine(bitValue.HasNegativeExtract
663+
? $"{parameters[0].ParameterName} = {bitValue.Addend} - ({parameters[0].ParameterType})extractedValue;"
664+
: $"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue - {bitValue.Addend};");
665+
}
666+
else
667+
{
668+
w.WriteLine($"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue;");
669+
}
670+
}
671+
else
672+
{
673+
writeSelector(w, selector, bitValue);
674+
}
675+
w.WriteLine("return true;");
676+
w.CloseBraceBlock();
677+
}
678+
}
679+
w.CloseBraceBlock();
680+
w.WriteLine("break;");
681+
}
682+
else
683+
{
684+
Debug.Assert(selector.Kind == EncodingSymbolSelectorKind.Masked);
621685

622-
if (selector.Kind == EncodingSymbolSelectorKind.Regular)
623-
{
624-
w.WriteLine($"switch (bitsToTest)");
625-
w.OpenBraceBlock();
626-
{
627-
foreach (var bitValue in selector.BitValues)
628-
{
629-
w.WriteLine($"case {bitValue.BitSelectorValue}:");
630-
w.OpenBraceBlock();
631-
if (bitValue.Kind == EncodingBitValueKind.BitExtract)
632-
{
633-
var bitRanges = bitValue.BitItems.Select(x => x.Range).ToList();
634-
var bitValueExtractString = ExtractBitRangeString(bitRanges, "bitValue", "extractedValue");
635-
Debug.Assert(parameters.Count == 1);
636-
w.WriteLine(bitValueExtractString);
637-
638-
if (bitValue.Addend != 0)
639-
{
640-
w.WriteLine(bitValue.HasNegativeExtract
641-
? $"{parameters[0].ParameterName} = {bitValue.Addend} - ({parameters[0].ParameterType})extractedValue;"
642-
: $"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue - {bitValue.Addend};");
643-
}
644-
else
645-
{
646-
w.WriteLine($"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue;");
647-
}
648-
}
649-
else
650-
{
651-
writeSelector(w, selector, bitValue);
652-
}
653-
w.WriteLine("return true;");
654-
w.CloseBraceBlock();
655-
}
656-
}
657-
w.CloseBraceBlock();
658-
w.WriteLine("break;");
659-
}
660-
else
661-
{
662-
Debug.Assert(selector.Kind == EncodingSymbolSelectorKind.Masked);
663-
664-
// Order from the highest number of bits sets to the lowest number
665-
// If the mask is 0, it means that all bits are sets
666-
var bitValues = selector.BitValues.OrderByDescending(x => BitOperations.PopCount(x.BitSelectorMask == 0 ? uint.MaxValue : x.BitSelectorMask)).ToList();
667-
668-
foreach (var bitValue in bitValues)
669-
{
670-
w.WriteLine(bitValue.BitSelectorMask == 0
671-
? $"if (bitsToTest == {bitValue.BitSelectorValue})"
672-
: $"if ((bitsToTest & 0x{bitValue.BitSelectorMask:x}) == {bitValue.BitSelectorValue})"
673-
);
674-
w.OpenBraceBlock();
675-
{
676-
if (bitValue.Kind == EncodingBitValueKind.BitExtract)
677-
{
678-
var bitRanges = bitValue.BitItems.Select(x => x.Range).ToList();
679-
var bitValueExtractString = ExtractBitRangeString(bitRanges, "bitValue", "extractedValue");
680-
Debug.Assert(parameters.Count == 1);
681-
w.WriteLine(bitValueExtractString);
682-
if (bitValue.Addend != 0)
683-
{
684-
w.WriteLine(bitValue.HasNegativeExtract
685-
? $"{parameters[0].ParameterName} = {bitValue.Addend} - ({parameters[0].ParameterType})extractedValue;"
686-
: $"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue - {bitValue.Addend};");
687-
}
688-
else
689-
{
690-
w.WriteLine($"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue;");
691-
}
692-
}
693-
else
694-
{
695-
writeSelector(w, selector, bitValue);
696-
}
697-
w.WriteLine("return true;");
698-
}
699-
w.CloseBraceBlock();
700-
}
701-
702-
w.WriteLine("break;");
703-
}
686+
// Order from the highest number of bits sets to the lowest number
687+
// If the mask is 0, it means that all bits are sets
688+
var bitValues = selector.BitValues.OrderByDescending(x => BitOperations.PopCount(x.BitSelectorMask == 0 ? uint.MaxValue : x.BitSelectorMask)).ToList();
704689

705-
w.CloseBraceBlock();
690+
foreach (var bitValue in bitValues)
691+
{
692+
w.WriteLine(bitValue.BitSelectorMask == 0
693+
? $"if (bitsToTest == {bitValue.BitSelectorValue})"
694+
: $"if ((bitsToTest & 0x{bitValue.BitSelectorMask:x}) == {bitValue.BitSelectorValue})"
695+
);
696+
w.OpenBraceBlock();
697+
{
698+
if (bitValue.Kind == EncodingBitValueKind.BitExtract)
699+
{
700+
var bitRanges = bitValue.BitItems.Select(x => x.Range).ToList();
701+
var bitValueExtractString = ExtractBitRangeString(bitRanges, "bitValue", "extractedValue");
702+
Debug.Assert(parameters.Count == 1);
703+
w.WriteLine(bitValueExtractString);
704+
if (bitValue.Addend != 0)
705+
{
706+
w.WriteLine(bitValue.HasNegativeExtract
707+
? $"{parameters[0].ParameterName} = {bitValue.Addend} - ({parameters[0].ParameterType})extractedValue;"
708+
: $"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue - {bitValue.Addend};");
709+
}
710+
else
711+
{
712+
w.WriteLine($"{parameters[0].ParameterName} = ({parameters[0].ParameterType})extractedValue;");
706713
}
707714
}
708-
w.CloseBraceBlock();
709-
w.WriteLine();
710-
foreach (var parameter in parameters)
715+
else
711716
{
712-
w.WriteLine($"{parameter.ParameterName} = default;");
717+
writeSelector(w, selector, bitValue);
713718
}
714-
715-
w.WriteLine("return false;");
719+
w.WriteLine("return true;");
716720
}
717721
w.CloseBraceBlock();
718722
}
723+
724+
w.WriteLine("break;");
719725
}
720-
w.CloseBraceBlock();
721726
}
722727

723728
private string ExtractBitRangeString(List<BitRange> bitRanges, string inputValue, string outputValue)

src/AsmArm64.CodeGen/InstructionProcessor.cs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
// Licensed under the BSD-Clause 2 license.
33
// See license.txt file in the project root for full license information.
44

5-
using System.Collections.Generic;
65
using System.Diagnostics;
76
using System.Runtime.InteropServices;
87
using System.Text;
9-
using System.Xml;
10-
using System.Xml.Linq;
118
using AsmArm64.CodeGen.Model;
129

1310
namespace AsmArm64.CodeGen;
@@ -20,8 +17,6 @@ internal sealed class InstructionProcessor
2017
private readonly InstructionSet _instructionSet;
2118
private readonly List<Instruction> _instructions;
2219
private readonly Dictionary<string, List<Instruction>> _memoryOperands = new();
23-
private readonly Dictionary<string, List<Instruction>> _immediateBitShiftSimdSpecial = new();
24-
2520
private readonly Dictionary<int, List<Instruction>> _operandCountToInstructions = new();
2621

2722
private readonly Dictionary<string, int> _barrierOperationLimitEnumValues = new();
@@ -425,7 +420,6 @@ private OperandDescriptor ProcessConst(Instruction instruction, ConstOperandItem
425420
Debug.Assert(symbol0.BitRanges.Count == 1);
426421
enumOperandDescriptor.EnumEncoding = symbol0.BitRanges[0];
427422

428-
Console.WriteLine($"{instruction.Id} {instruction.FullSyntax}");
429423
return enumOperandDescriptor;
430424
}
431425
else if (instruction.Id == "STSHH_hi_hints")
@@ -976,13 +970,38 @@ private RegisterOperandDescriptor ProcessRegister(Instruction instruction, Regis
976970
Debug.Assert(false, $"No encoding found for {instruction.Id}");
977971
}
978972

973+
descriptor.IndexerExtract = ProcessIndexer(instruction, register);
974+
979975
if (kind == Arm64RegisterEncodingKind.V && register.TextElements.Count > 1)
980976
{
981977
descriptor.VectorArrangementLocalIndex = GetVectorArrangementLocalIndex(instruction, register);
982978
}
983979

984-
descriptor.IndexerExtract = ProcessIndexer(instruction, register);
985-
980+
//if (descriptor.IndexerExtract is not null)
981+
//{
982+
// if (descriptor.VectorArrangementLocalIndex != 0)
983+
// {
984+
// var vectorExtract = instruction.VectorArrangements[descriptor.VectorArrangementLocalIndex - 1];
985+
// Console.WriteLine($"Indexer {register} - {instruction.Id} {instruction.FullSyntax} -> ");
986+
987+
// var selector = vectorExtract.Selector;
988+
// if (selector is not null)
989+
// {
990+
// var names = selector.BitValues.SelectMany(x => x.Text).ToHashSet();
991+
992+
// foreach (var name in names.Order())
993+
// {
994+
// Console.WriteLine($" {name}");
995+
// }
996+
// }
997+
// else
998+
// {
999+
// Console.WriteLine($" {vectorExtract.Kind}");
1000+
1001+
// }
1002+
// }
1003+
//}
1004+
9861005
return descriptor;
9871006
}
9881007

@@ -1129,6 +1148,22 @@ private RegisterGroupOperandDescriptor ProcessRegisterGroup(Instruction instruct
11291148

11301149
registerGroup.IndexerExtract = ProcessIndexer(instruction, group);
11311150

1151+
//if (registerGroup.IndexerExtract is not null)
1152+
//{
1153+
// Console.WriteLine($"GROUP {group} - {instruction.Id} {instruction.FullSyntax} -> ");
1154+
1155+
// var selector = registerGroup.IndexerExtract.Selector;
1156+
// if (selector is not null)
1157+
// {
1158+
// var names = selector.BitValues.SelectMany(x => x.Text).ToHashSet();
1159+
1160+
// foreach (var name in names.Order())
1161+
// {
1162+
// Console.WriteLine($" {name}");
1163+
// }
1164+
// }
1165+
//}
1166+
11321167
return registerGroup;
11331168
}
11341169

src/AsmArm64/Arm64RegisterAny.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,30 @@ namespace AsmArm64;
99
/// <summary>
1010
/// Represents an ARM64 register.
1111
/// </summary>
12-
public readonly record struct Arm64RegisterAny : IArm64Register
12+
public readonly record struct Arm64RegisterAny : IArm64RegisterVPacked, IArm64RegisterVIndexed
1313
{
1414
private readonly uint _value;
1515

1616
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1717
private Arm64RegisterAny(uint index) => _value = index;
1818

19-
public static Arm64RegisterAny Create(Arm64RegisterKind kind, int index) => new((uint) kind << 8 | (uint) index);
20-
21-
public static Arm64RegisterAny Create(Arm64RegisterKind kind, Arm64RegisterVKind vKind, int elementCount, int index) => new( (uint)elementCount << 24 | (uint)vKind << 16 | (uint)kind << 8 | (uint)index);
19+
public static Arm64RegisterAny Create(Arm64RegisterKind kind, int index, Arm64RegisterVKind vKind, int elementCount, int elementIndex) => new(((uint)elementIndex << 28) | ((uint)(elementCount >> 1) << 24) | ((uint)vKind << 16) | ((uint)vKind << 8) | (uint)index);
2220

2321
/// <inheritdoc />
2422
public Arm64RegisterKind Kind => (Arm64RegisterKind) (_value >> 8);
2523

24+
/// <inheritdoc />
25+
public Arm64RegisterVKind VKind => (Arm64RegisterVKind)(_value >> 16);
26+
2627
/// <inheritdoc />
2728
public int Index => (byte) _value;
2829

30+
/// <inheritdoc />
31+
public int ElementCount => (byte)(((_value >> 24) & 0xF) << 1);
32+
33+
/// <inheritdoc />
34+
public int ElementIndex => (byte)(_value >> 28);
35+
2936
/// <inheritdoc />
3037
public override string ToString() => ToString(null, null);
3138

@@ -57,6 +64,10 @@ public static string ToText(this Arm64RegisterAny register, bool upper = false)
5764
Arm64RegisterKind.V => Unsafe.BitCast<Arm64RegisterAny, Arm64RegisterV>(register).ToText(upper),
5865
Arm64RegisterKind.VTyped => throw new NotImplementedException(),
5966
Arm64RegisterKind.VScalar => throw new NotImplementedException(),
67+
Arm64RegisterKind.C => throw new NotImplementedException(),
68+
Arm64RegisterKind.VPacked => throw new NotImplementedException(),
69+
Arm64RegisterKind.VPackedIndexed => throw new NotImplementedException(),
70+
Arm64RegisterKind.VTypedIndexed => throw new NotImplementedException(),
6071
_ => throw new ArgumentOutOfRangeException()
6172
};
6273
}

0 commit comments

Comments
 (0)