.NET Core:虚拟机上的x86_64本征

我们生活在x86架构占主导地位的时代。所有与x86兼容的处理器都相似,但是都略有不同。不仅是制造商,频率和芯数。

x86体系结构在存在(和流行)期间经历了许多重大更新(例如,对64位的扩展-x86_64)和“扩展指令集”的添加。编译器默认情况下会生成所有处理器尽可能通用的代码,但编译器也必须适应这一情况。但是在扩展说明中,有许多有趣且有用的内容。例如,在国际象棋程序中,经常使用处理位的指令:POPCNT,BSF / BSR(或更近的类似物TZCNT / LZCNT),PDEP,BSWAP等。

在C和C ++编译器中,通过“此处理器的固有功能”实现对此类指令的显式访问。example1 example2

对于.NET和C#没有如此方便的访问,因此曾经一段时间我制作了自己的包装程序,提供了此类功能的仿真,但是如果CPU支持它们,我将直接在调用代码中替换它们的调用。幸运的是,我需要的大多数内在函数都放在了CALL操作码的5个字节中。可以在该链接上的中心上阅读详细信息

从那时起已经过去了很多年,在.NET中,普通内在函数从未出现过。但是.NET Core出现了,这种情况已得到纠正。首先是矢量指令,然后是几乎整个System.Runtime.Intrinsics.X86
*-没有“过时的” BSF和BSR

,一切似乎都很方便。除了对每组指令的支持的定义一直令人困惑(某些指令立即包含在指令集中,对于某些指令则有单独的标志)。因此,.NET Core在“允许”集之间还存在一些依赖关系这一事实使我们更加困惑。

当我尝试在具有KVM虚拟机管理程序的虚拟机上运行代码时,这种情况浮出水面:错误发生在System.PlatformNotSupportedException: Operation is not supported on this platform at System.Runtime.Intrinsics.X86.Bmi1.X64.TrailingZeroCount(UInt64 value)。对于System.Runtime.Intrinsics.X86.Popcnt.X64.PopCount同样如此。但是,如果对于POPCNT来说,可以在虚拟化参数中加入一个明显的标志,那么TZCNT会让我陷入困境。在下图中,该工具的输出检查netcore(在本文结尾处的代码和二进制文件)和知名的CPU-Z中内在函数的可用性:



这是从MSDN页面获取的关于CPUID的工具的输出



尽管处理器报告支持所有功能需要时,指令Intrinsics.X86.Bmi1.X64.TrailingZeroCount仍然随着执行而下降System.PlatformNotSupportedException

为了弄清楚这一点,我们需要从NETCore的角度看待处理器。哪些资源位于github上。让我们在那里寻找丘比特,然后使用该方法,EEJitManager::SetCpuInfo()

其中有很多不同的条件,其中有些是嵌套的。我采用了这种方法,并将其复制到一个空项目中。除此之外,我还必须选择其他几种方法和一个完整的汇编文件(如何将asm添加到新工作室)。执行结果:



如您所见,该标志InstructionSet_BMI1仍处于设置状态(尽管其他一些未设置)。

如果您在存储库中查找此标志,则可能会遇到以下代码

if (resultflags.HasInstructionSet(InstructionSet_BMI1) && !resultflags.HasInstructionSet(InstructionSet_AVX))
    resultflags.RemoveInstructionSet(InstructionSet_BMI1);

所以,她是我们的瘾!如果未定义AVX,则禁用BMI1(和其他一些设置)。逻辑是什么,我尚不清楚,但我们希望它仍然存在。现在仍然需要了解为什么cpu-z和其他工具可以看到AVX,而netcore却不能。

让我们看看我们的工具在不同处理器上的输出如何不同:

>diff a b
7c7,8
< Test ((buffer[8] & 0x02) != 0) -> 0
---
> Test ((buffer[8] & 0x02) != 0) -> 1
> ==> Set InstructionSet_PCLMULQDQ
18c19,32
< Test ((buffer[11] & 0x18) == 0x18) -> 0
---
> Test ((buffer[11] & 0x18) == 0x18) -> 1
> Test (hMod == NULL) -> 0
> Test (pfnGetEnabledXStateFeatures == NULL) -> 0
> Test ((FeatureMask & XSTATE_MASK_AVX) == 0) -> 0
> Test (DoesOSSupportAVX() && (xmmYmmStateSupport() == 1)) -> 1
> Test (hMod == NULL) -> 0
> Test (pfnGetEnabledXStateFeatures == NULL) -> 0
> Test ((FeatureMask & XSTATE_MASK_AVX) == 0) -> 0
> ==> Set InstructionSet_AVX
> Test ((buffer[9] & 0x10) != 0) -> 1
> ==> Set InstructionSet_FMA
> Test (maxCpuId >= 0x07) -> 1
> Test ((buffer[4] & 0x20) != 0) -> 1
> ==> Set InstructionSet_AVX2

  1. 检查缓冲区[8]和0x02失败,这是PCLMULQDQ
  2. 缓冲区[11]和0x18失败,它是AVX和OSXSAVE,已经设置了AVX(CPU-Z看到此信息),需要OSXSAVE
  3. 后面还有其他导致InstructionSet_AVX标志的检查

那么如何处理病毒?如果可能的话,最好将libvirt.cpu_mode放置在host-passthrough或host-model中

但是,如果这不可能,那么您必须特别添加说明中的所有汤ssse3, sse4.1, sse4.2, sse4a, popcnt, abm, bmi1, bmi2, avx, avx2, osxsave, xsave, pclmulqdq在这里我打个招呼,谢谢vdsina_m;)

并且您可以检查主机或虚拟机的指令支持,以及.NET Core在此工具的帮助下如何看待它:(目前为zip,我稍后将其发布到github)。


All Articles