aboutsummaryrefslogtreecommitdiff
path: root/src/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs
blob: 4f6f1e87b16db76557e0b054e302d00fa95b3340 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using Ryujinx.Memory;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;

namespace ARMeilleure.CodeGen.X86
{
    static class HardwareCapabilities
    {
        private delegate uint GetXcr0();

        static HardwareCapabilities()
        {
            if (!X86Base.IsSupported)
            {
                return;
            }

            (int maxNum, _, _, _) = X86Base.CpuId(0x00000000, 0x00000000);

            (_, _, int ecx1, int edx1) = X86Base.CpuId(0x00000001, 0x00000000);
            FeatureInfo1Edx = (FeatureFlags1Edx)edx1;
            FeatureInfo1Ecx = (FeatureFlags1Ecx)ecx1;

            if (maxNum >= 7)
            {
                (_, int ebx7, int ecx7, _) = X86Base.CpuId(0x00000007, 0x00000000);
                FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
                FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7;
            }

            Xcr0InfoEax = (Xcr0FlagsEax)GetXcr0Eax();
        }

        private static uint GetXcr0Eax()
        {
            if (!FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave))
            {
                // XSAVE feature required for xgetbv
                return 0;
            }

            ReadOnlySpan<byte> asmGetXcr0 = new byte[]
            {
                0x31, 0xc9, // xor ecx, ecx
                0xf, 0x01, 0xd0, // xgetbv
                0xc3, // ret
            };

            using MemoryBlock memGetXcr0 = new((ulong)asmGetXcr0.Length);

            memGetXcr0.Write(0, asmGetXcr0);

            memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);

            var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);

            return fGetXcr0();
        }

        [Flags]
        public enum FeatureFlags1Edx
        {
            Sse = 1 << 25,
            Sse2 = 1 << 26,
        }

        [Flags]
        public enum FeatureFlags1Ecx
        {
            Sse3 = 1 << 0,
            Pclmulqdq = 1 << 1,
            Ssse3 = 1 << 9,
            Fma = 1 << 12,
            Sse41 = 1 << 19,
            Sse42 = 1 << 20,
            Popcnt = 1 << 23,
            Aes = 1 << 25,
            Xsave = 1 << 26,
            Osxsave = 1 << 27,
            Avx = 1 << 28,
            F16c = 1 << 29,
        }

        [Flags]
        public enum FeatureFlags7Ebx
        {
            Avx2 = 1 << 5,
            Avx512f = 1 << 16,
            Avx512dq = 1 << 17,
            Sha = 1 << 29,
            Avx512bw = 1 << 30,
            Avx512vl = 1 << 31,
        }

        [Flags]
        public enum FeatureFlags7Ecx
        {
            Gfni = 1 << 8,
        }

        [Flags]
        public enum Xcr0FlagsEax
        {
            Sse = 1 << 1,
            YmmHi128 = 1 << 2,
            Opmask = 1 << 5,
            ZmmHi256 = 1 << 6,
            Hi16Zmm = 1 << 7,
        }

        public static FeatureFlags1Edx FeatureInfo1Edx { get; }
        public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
        public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0;
        public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0;
        public static Xcr0FlagsEax Xcr0InfoEax { get; } = 0;

        public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse);
        public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2);
        public static bool SupportsSse3 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse3);
        public static bool SupportsPclmulqdq => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Pclmulqdq);
        public static bool SupportsSsse3 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Ssse3);
        public static bool SupportsFma => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Fma);
        public static bool SupportsSse41 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse41);
        public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
        public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
        public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
        public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128);
        public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
        public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave)
            && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128 | Xcr0FlagsEax.Opmask | Xcr0FlagsEax.ZmmHi256 | Xcr0FlagsEax.Hi16Zmm);
        public static bool SupportsAvx512Vl => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512vl) && SupportsAvx512F;
        public static bool SupportsAvx512Bw => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512bw) && SupportsAvx512F;
        public static bool SupportsAvx512Dq => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512dq) && SupportsAvx512F;
        public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c);
        public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha);
        public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni);

        public static bool ForceLegacySse { get; set; }

        public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
        public static bool SupportsEvexEncoding => SupportsAvx512F && !ForceLegacySse;
    }
}