MOVDQU指令+页面边界

use*_*291 11 linux sse4

我有一个简单的测试程序,它使用movdqu指令加载一个xmm寄存器,访问页面边界(OS = Linux)的数据.

如果映射了以下页面,这可以正常工作.如果它没有映射,那么我得到一个SIGSEGV,这可能是预期的.

然而,这会大大减少未对齐载荷的有用性.另外,允许未对齐的内存引用的SSE4.2指令(如pcmpistri)似乎也表现出这种行为.

这一切都很好 - 除了有很多使用pcmpistri的strcmp实现,我发现它似乎根本没有解决这个问题 - 而且我已经能够设计出导致这些实现失败的琐碎测试用例,而对于一次一个字节的简单strcmp实现将在相同的数据布局下正常工作.

还有一点需要注意 - 64位Linux的GNU C库实现似乎有一个__strcmp_sse42变体,它似乎以更安全的方式使用pcmpistri指令.这个strcmp的实现相当复杂,但它似乎正在仔细地试图避免页面边界问题.我不确定这是否是由于我上面描述的问题,或者它是否只是通过对齐数据来尝试获得更好性能的副作用.

无论如何,我的问题主要是 - 我在哪里可以找到关于这个问题的更多信息?我输入了"movdqu crossing page boundary"和我可以想到的每一个变体,但没有遇到任何特别有用的东西.如果有人能指出我进一步的信息,将不胜感激.

小智 8

首先,任何试图访问未映射地址的算法都会导致SegFault.如果非AVX代码流使用4字节加载来访问页面的最后一个字节,并且"下一页"的前3个字节恰好没有映射,那么它也会导致SegFault.没有?我认为"问题"是AVX(1/2/3)寄存器比"典型"寄存器大得多,以至于如果它们被简单地扩展到更大的寄存器,那么它们就会被不安全(但是侥幸逃脱)的算法被捕获.

对齐负载(MOVDQA)永远不会出现这个问题,因为它们不会跨越自己大小或更大的任何边界.未对齐的负载可能会出现此问题(正如您所指出的那样)并且"经常"这样做.原因是该指令被定义为加载目标寄存器的完整大小.您需要非常仔细地查看指令定义中的操作数类型.无论你感兴趣的数据有多少都无关紧要.重要的是指令的定义.

然而...

AVX1(Sandybridge)增加了一个"蒙面移动"功能,它比movdqa或movdqu慢,但是不会(在架构上)访问未映射的页面,只要没有为该页面中的访问部分启用掩码.这是为了解决这个问题.通常,向前移动,看起来加载/存储的屏蔽部分(参见AVX512)也不会导致IA上的访问冲突.

(这是关于PCMPxSTRx行为的一个笨蛋.也许你可以在你的"字符串"对象中添加15个字节的填充?)