我正在将代码从ICU 58.2移植到ICU 59.1,在那里他们将字符类型从uint16_t更改为char16_t。我本来打算做一个直接的reinterpret_cast,在这里我需要转换这些类型,但发现ICU59.1实际上提供了这种转换的功能。我不明白为什么他们需要在做reinterpret_cast之前使用这个防混叠屏障。
#elif (defined(__clang__) || defined(__GNUC__)) && U_PLATFORM !=
U_PF_BROWSER_NATIVE_CLIENT
# define U_ALIASING_BARRIER(ptr) asm volatile("" : : "rm"(ptr) : "memory")
#endif..。
inline const UChar *toUCharPtr(const char16_t *p) {
#ifdef U_ALIASING_BARRIER
U_ALIASING_BARRIER(p);
#endif
return reinterpret_cast<const UChar *>(p);为什么在不调用reinterpret_cast的情况下使用U_ALIASING_BARRIER就不安全?
发布于 2017-09-20 19:34:41
据猜测,这是为了阻止任何违反strict aliasing rule的行为,这可能发生在调用尚未完全清理的代码中,从而导致优化时出现意外行为(这一点的提示在上面的注释中:“即使跨越函数边界也是指针反混叠优化的障碍”)。
严格的别名规则禁止在指针具有不兼容类型时别名相同值的指针(一个C概念,但C++用更多的单词表示类似的内容)。这里有一个小问题:char16_t和uint16_t不需要兼容。uint16_t实际上是一个可选的支持类型(在C和C++中);char16_t等同于uint_least16_t,而uint_least16_t不一定是相同的类型。它在x86上将具有相同的宽度,但是编译器不需要将其标记为实际上是相同的东西。它甚至可能故意松懈,假设类型通常表示不同的意图可以别名。
在链接的答案中有一个更完整的解释,但基本上给出了如下代码:
uint16_t buffer[] = ...
buffer[0] = u'a';
uint16_t * pc1 = buffer;
char16_t * pc2 = (char16_t *)pc1;
pc2[0] = u'b';
uint16_t c3 = pc1[0];...if由于任何原因编译器没有将char16_t和uint16_t标记为兼容,而且您正在编译包含其等效的-fstrict-aliasing的优化,允许假设pc2不能修改pc1点,而不是在将其赋值给c3之前重新加载值,可能会给它u'a'。
与示例类似的代码可能在转换过程中出现,在转换过程中,以前的代码在任何地方都很高兴地使用uint16_t *,但是现在在代码块的顶部提供了一个char16_t *,以便与ICU 59兼容,然后所有下面的代码都被完全更改为只通过正确类型的指针读取。
由于编译器通常不会优化手工编码的程序集,asm块的存在将迫使它检查所有关于寄存器和其他临时值状态的假设,并在U_ALIASING_BARRIER之后第一次取消引用时对每个值进行完全重新加载,而不管是否存在优化标志。如果您继续在转换下面写入uint16_t *,这将不会保护您免受任何进一步的别名问题的影响(如果您这样做,这完全是您自己的错误),但它至少应该确保转换调用之前的状态不会以一种可能导致写入新指针的方式在之后意外跳过。
https://stackoverflow.com/questions/46289805
复制相似问题