【问题标题】:Why is Xcode/LLVM optimising out the variable this method returns when compiling for ARM64为什么 Xcode/LLVM 在为 ARM64 编译时优化此方法返回的变量
【发布时间】:2014-03-05 14:16:14
【问题描述】:

这是一个已知错误,已在错误 ID #16040090

下提交给 bugreporter.apple.com

注意:此问题只能在 64 位 (A7) iOS 设备上重现。如果在模拟器或 32 位设备上运行,它可以正常工作。

请注意:此错误似乎已在 LLVM 5.1 中修复,这是 Xcode 5.1 的默认编译器(目前处于测试阶段)。

我有一个方法-pointValue(见下文),它在优化级别设置为-O0 的情况下运行良好,但使用-Os(Release 构建的默认值)编译器会优化设置的行变量pointpoint 用作返回值,因此任何其他调用它的方法都会获得随机值。

可以通过将points 变量声明为volatile 来解决此问题。但是,为什么编译器认为它可以删除行设置points?和我写方法有关系吗?

- (CGPoint)pointValue
{
    ULongLong encodedPoint = [self unsignedLongLongValue];
    ULongLong *values = [self splitEncodedInteger:encodedPoint withShift:SHIFT];
    
    //volatile is required to prevent optimising out
    volatile CGPoint point = CGPointMake(values[0], values[1]);
    
    free(values); //clean up
    
    return point;
}

注意: -splitEncodedInteger:withShift: 使用 malloc() 创建一个 2 元素数组,该数组被重新调整,然后此内存由被调用者 free'd。

根据要求,-splitEncodedInteger:withShift:的代码

- (ULongLong *)splitEncodedInteger:(ULongLong)encocedInteger withShift:(int)shiftValue
{
    ULongLong *splitFloats = malloc(sizeof(ULongLong) * 2);
    
    splitFloats[0] = (encocedInteger >> shiftValue);
    splitFloats[1] = encocedInteger - ((encocedInteger >> shiftValue) << shiftValue);
    
    return splitFloats;
}

编码类别的完整source code可以是found on GitHub

示例项目

应几位评论者的要求,我创建了一个simple test project但是,即使在将所有构建设置设置为与原始项目匹配之后,我自己无法重现该问题。该错误仅显示在设备上

我唯一能看到不同的是,当项目在完全优化的情况下运行时,LLDB 可以读取一些变量,并尝试将它们打印到控制台我得到这个:

(CGSize) size = <no location, value may have been optimized out>

这正是我在失败项目中得到的结果,但在这种情况下,传递这些“优化”变量之一只会给出随机数据(导致创建大量视图 [或创建失败])。

以下是分别使用 -O0 和 -Ofast 运行的示例应用的屏幕截图。

使用 -O0 运行的示例应用(无优化),您可以查看有关变量的信息

使用 -Ofast 运行的示例应用(完全优化),您无法查看有关变量的信息

在设备上运行

这是在 优化关闭的情况下运行 ton 设备时的输出:

2014-02-10 11:36:00.771 optimisation-bug[461:60b] *** Compiling with no optmisations, the code should work as expected ***
2014-02-10 11:36:00.773 optimisation-bug[461:60b] encodedRect: 50332672
2014-02-10 11:36:00.774 optimisation-bug[461:60b] encodedSize: 45089712
2014-02-10 11:36:00.775 optimisation-bug[461:60b] encodePoint: 2621480
2014-02-10 11:36:05.629 optimisation-bug[461:60b] rect: {{0, 0}, {768, 1024}}
2014-02-10 11:36:06.099 optimisation-bug[461:60b] size: {688, 944}
2014-02-10 11:36:06.101 optimisation-bug[461:60b] point: 40.000000, 40.000000
2014-02-10 11:36:06.103 optimisation-bug[461:60b] DONE

这是在具有全面优化的设备上运行时的输出:

2014-02-10 11:44:53.975 optimisation-bug[471:60b] >>> Compiling with -Os optmisations, the code should *NOT* work as expected <<<
2014-02-10 11:44:53.977 optimisation-bug[471:60b] encodedRect: 50332672
2014-02-10 11:44:53.977 optimisation-bug[471:60b] encodedSize: 45089712
2014-02-10 11:44:53.978 optimisation-bug[471:60b] encodePoint: 2621480
2014-02-10 11:44:58.176 optimisation-bug[471:60b] rect: {{1.3464973428307575e+19, 6174053600}, {1.3464973428307575e+19, 1.2554206452792682e+58}}
2014-02-10 11:44:58.178 optimisation-bug[471:60b] size: {1.3464973428307575e+19, 6174053568}
2014-02-10 11:44:58.180 optimisation-bug[471:60b] point: 13464973428307574784.000000, 6174053568.000000
2014-02-10 11:44:58.182 optimisation-bug[471:60b] DONE

【问题讨论】:

  • 你对比过生成的程序集吗?你在这里所做的看起来没问题。问题可能不是你想的那样。否则,在我看来,它就像一个编译器错误。
  • @rjstelling - 在 10.8 上使用 Xcode 5.0.2,您的代码在没有 volatile 的情况下运行良好。调试器无法显示所有值,但这并不少见,但NSLog 确实如此。所以其他东西似乎会导致你的问题。顺便说一句,我建议您最好使用 uint64 作为您的类型,而不是假设 unsigned long long 是 64 位。还可以使用包含两个字段的 struct,而不是 mallocing 和 freeing 两个元素的数组 - structs 是按值传递的,您不需要内存管理。
  • @rjstelling 您的问题的标题是“为什么 LLVM 会优化变量...”,在我看来,我链接的帖子中的答案给出了发生这种情况的各种情况,另外他们指出优化后的变量进入寄存器,因此仍然是一个值。 “优化出来”不破坏价值!因此,我的“重复”建议旨在暗示问题可能是其他问题,优化/易失性是一个红鲱鱼。
  • 正如@CRD 所指出的,我试图用你的类别重现这个,但到目前为止我还没有看到它,无论是在输出中还是在程序集中。你有一个失败的明确测试用例吗?它是否可能仅在 32 位操作系统上失败? (CRD 和我正在 64 位操作系统上进行测试。)
  • @RobNapier 经Mike Ash 确认仅是 64 位问题。

标签: objective-c xcode arm llvm compiler-optimization


【解决方案1】:

这个问题似乎是 arm64 编译器中的一个错误; armv7(s) 和 x86 (Simulator) 运行代码没有问题。

- (CGPoint)pointValue
{
    ULongLong encodedPoint = [self unsignedLongLongValue];
    ULongLong *values = [self splitEncodedInteger:encodedPoint withShift:SHIFT];

    //volatile is required to prevent optimising out
    /*volatile*/ CGPoint point = CGPointMake(values[0], values[1]);

    free(values); //clean up

    return point;
}

似乎正在发生的事情是free(values) 行在优化阶段被重新排序并在CGPointMake(...) 之前运行,导致pointer 返回垃圾值。

感谢Mike AshCRDRob Napier

【讨论】:

  • 哇。感谢您坚持这么久,以便能够如此具体地追踪它。
猜你喜欢
  • 2015-09-15
  • 1970-01-01
  • 2013-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多