Android NDK:ARMv6 + VFP设备。错误的计算,NaN,非正规数,VFP11错误

时间:2012-12-13 23:59:53

标签: android-ndk arm nan armv6

我希望使用VFP Android设备定位ARMv6。

我的Android.mk文件中有以下行以启用VFP

LOCAL_CFLAGS    := -marm -mfloat-abi=softfp -mfpu=vfp -Wmultichar

我相信我使用ARMv5定位VFP

我修改了android-ndk-r8b\toolchains\arm-linux-androideabi-4.6\setup.mk以删除-msoft-float。我还尝试使用原始setup.mk

我的代码工作正常99.99%,但有些时候在ARMv6设备上疯狂。 我有特殊的代码来检测它什么时候发疯。

代码

glm::vec3 D = P1 - P2;
float f1 = sqrtf(D.x*D.x + D.y*D.y + D.z*D.z);
if(!(f1 < 5)){
    // f1 is bigger then 5 or NaN
    mylog_fmt("Crazy %f %f %f %f", P1.x, P1.y, P1.z, f1);
    mylog_fmt("%f %f %f", P2.x, P2.y, P2.z);
}

logcat的

12-14 00:59:08.214: I/APP(17091): Crazy -20.000031 0.000000 0.000000 20.000000
12-14 00:59:08.214: I/APP(17091): -20.000000 0.000000 0.000000

计算2点之间的距离。通常是0.000031 但是当crazy mode开启时,它是20.0

在ARMv7 CPU上运行时,问题不存在。它仅存在于ARMv6 CPU上。

我认为它应该是与编译器设置或版本相关的一些常见的已知错误。可能是代码缺少内存障碍。

我想看到一些类似错误的参考。解决方法。或者关于bug的性质。

当ARMv7上的相同代码没有给出NaN时,我也常常在ARMv6上获得NaN值。

我已经调试了2周的代码并在网上搜索。如果有人可以分享类似问题的链接,那将是一个很大的帮助!

PS。这是一个编译命令的示例。我已经尝试了很多不同的设置。

编译器设置

c:/soft/Android/android-ndk-r8b/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/arm-linux-androideabi-g++
-MMD -MP -MF ./obj/local/armeabi/objs/main/sys/base.o.d -fpic -ffunction-sections -funwind-tables -fstack-protector 
-D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__
-D__ARM_ARCH_5TE__  
-march=armv5te -mtune=arm6 
-mfloat-abi=softfp -mfpu=vfp
-fno-exceptions -fno-rtti -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 
-Ijni/main/ -Ijni/main/sys -Ijni/main/bullet/src -Ijni/main/bullet/src/LinearMath -Ijni/main/bullet/src/BulletCollision/BroadphaseCollision 
-Ijni/main/bullet/src/BulletCollision/CollisionDispatch -Ijni/main/bullet/src/BulletCollision/CollisionShapes -Ijni/main/bullet/src/BulletCollision/NarrowPhaseCollision 
-Ijni/main/bullet/src/BulletDynamics/ConstraintSolver -Ijni/main/bullet/src/BulletDynamics/Dynamics -Ijni/main/../libzip/ -Ic:/soft/Android/android-ndk-r8b/sources/cxx-stl/stlport/stlport 
-Ic:/soft/Android/android-ndk-r8b/sources/cxx-stl//gabi++/include -Ijni/main 
-DANDROID

-marm -march=armv6 -mfloat-abi=softfp -mfpu=vfp -Wmultichar

-Wa,--noexecstack  -frtti  -O2 -DNDEBUG -g   -Ic:/soft/Android/android-ndk-r8b/platforms/android-5/arch-arm/usr/include -c  jni/main/sys/base.cpp
-o ./obj/local/armeabi/objs/main/sys/base.o

更新2

所有这些设备都配有Qualcomm MSM7227A 它有ARM1136JF-S

到目前为止我学到的是这个bug可能与de-norms有关 我读到ARMv7与ARMv6的差异,默认情况下denorms刷新为零,ARM1136SF-S可选。 http://infocenter.arm.com/help/topic/com.arm.doc.ddi0211k/DDI0211K_arm1136_r1p5_trm.pdf

尚不确定如何验证ARM上的Flush-To-ZERO标志。

更新3

此CPU的VFP称为VFP11 我找到了--vfp11-denorm-fix选项。 还有--vfp-denorm-fix 他们纠正了VFP11 cpus中的错误。看起来像我的目标问题。 找到关于VFP11错误的一些帖子。希望它能修复代码。

2 个答案:

答案 0 :(得分:2)

好像我发现了bug。

这是VFP11(ARMv6协处理器)denorm bug中的错误。 denormal numbers数字很小。

我在使用转储实现spring的物理代码中得到了这个数字

force1 = (Center - P1) * k1         // force1 directed to center 
force2 = - Velocity * k2            // force2 directed against velocity
Object->applyForce(force1)
Object->applyForce(force2)

当物品进入Center并且最后得到denormal值时,两种力都变得非常小。

我可以重写sring和dumping但是我不能重新编写一个BulletPhysics洞或所有数学代码并预测每个(甚至内部)非正规数的出现。

链接器已修复代码选项--vfp11-denorm-fix--vfp-denorm-fix http://sourceware.org/binutils/docs-2.19/ld/ARM.html

NDK链接器--vfp11-denorm-fix 这个选项有帮助。代码看起来更可重复,但它不能解决100%的问题。

我现在看到了更少的错误。

如果我等待sping稳定物体然后我终于得到denorm - &gt;为NaN

我必须等待更长时间,但同样的问题到了。

如果您知道解决方案会修复像--vfp11-denorm-fix这样的代码,那么我会给您赏金。

我尝试了--vfp11-denorm-fix=scalar--vfp11-denorm-fix=vector

刷新到零位

      int x;
      // compiles in ARM mode
      asm(
              "vmrs %[result],FPSCR \r\n"
              "orr %[result],%[result],#16777216 \r\n"
              "vmsr FPSCR,%[result]"
              :[result] "=r" (x) : :
      );

不确定原因,但在LOCAL_ARM_MODE := arm中需要Android.mk -mfpu=vfp-d16,而不仅仅是vfp

手动清除非正常数字

我有上面描述的弹簧代码。 我通过手动清除非正规数而不使用具有以下功能的FPU来改进它。

inline void fixDenorm(float & f){
    union FloatInt32 {
        unsigned int u32;
        float f32;
    };
        FloatInt32 fi;
        fi.f32 = f;

        unsigned int exponent = (fi.u32 >> 23) & ((1 << 8) - 1);
        if(exponent == 0)
            f = 0.f;
}

原始代码在许多地方从15-90秒开始失败。

目前的代码在物理模拟10分钟后,只显示了一个可能与此错误有关的问题。


参考bug和修复 http://sourceware.org/ml/binutils/2006-12/msg00196.html

他们说GCC仅使用scalr代码而--vfp11-denorm-fix=scalar就足够了。 它增加了1个额外的命令来减速。但即使增加2个额外命令的--vfp11-denorm-fix=vector也是不够的。

问题不易重新制作。在频率较高的800Mhz的手机上,我会更频繁地看到它,然后在较慢的600Mhz上。当市场上没有快速CPU时,可能会进行修复。


项目中有很多文件,每个配置编辑大约需要10分钟。 使用当前的修复状态进行测试需要大约10分钟才能在手机上播放。 +我们在灯下加热手机。热门手机可以更快地显示错误。

我希望测试不同的配置并报告最有效的修复方法。但是现在我们必须添加hack来杀死可能与denorms有关的最后一个bug。

我希望找到可以修复它的银弹,但只有-msoft-float性能下降10倍或在ARMv7上运行应用程序就可以了。


在Spring / dumping代码中用新的fixDenorm替换之前的fixDenormE函数并为ViewMatrix应用新函数后,我摆脱了最后一个错误。

inline void fixDenormE(float & f, float epsilon = 1e-8){
    union Data32 {
        unsigned int u32;
        float f32;
    };
        Data32 d;
        d.f32 = f;

        unsigned int exponent = (d.u32 >> 23) & ((1 << 8) - 1);
        if(exponent == 0)
            f = 0.f;
        if(fabsf(f) < epsilon){
          f = 0.f;
        }
}

答案 1 :(得分:0)

此页面讨论了ARM FPU选项:VfpComparison

我认为如果你想为ARM v6构建,你可以这样做:-march=armv6 -mcpu=generic-armv6 -mfloat-abi=softfp(并省略-mfpu选项)。如果您没有专门针对上面提到的处理器,通用armv6没有保证的fpu。

另一种选择是尝试-mfloat-abi=hard,基于softfp周围存在编译器错误的理论。

同时检查代码中是否存在任何堆栈损坏等,有可能在传递浮点值时会破坏它们。

P.S。您可能还想尝试一个浮点测试器,例如TestFloat或古老的netlib paranoia。虽然您在此特定处理器上有一个浮点失败的示例,并且使用这些编译器选项,但您不知道问题的普遍程度。它可能比你想象的更糟糕:)