为 ARM NEON 编译时出现未知的 GCC 错误(严重)

Posted

技术标签:

【中文标题】为 ARM NEON 编译时出现未知的 GCC 错误(严重)【英文标题】:Unknown GCC error, while compiling for ARM NEON (Critical) 【发布时间】:2010-09-28 08:59:22 【问题描述】:

我有一个基于 ARM NEON Cortex-A8 的处理器目标。我正在通过使用 NEON 来优化我的代码。但是当我编译我的代码时,我得到了这个奇怪的错误。不知道如何解决这个问题。

我正在尝试在我的主机上使用 Code Sourcery (PART2) 编译以下代码 (PART 1)。 我得到了这个奇怪的错误(第 3 部分)。我在这里做错了吗?其他人可以编译这个,看看他们是否也得到相同的编译错误?

奇怪的是,如果我在代码中注释掉代码中的else if(step_size == 4) 部分,那么错误就会消失。但是,很遗憾没有它我的优化是不完整的,所以我必须拥有它。

起初我认为是 CodeSourcey 编译器(在我的主机上)的问题,所以我直接在我的目标上编译了程序(我的目标在 Ubuntu 上运行)。我在那里再次使用 gcc,我得到了同样的错误,当我注释掉 else if(step_size == 4) 部分时,错误消失了。

救命!


第 1 部分

#include<stdio.h>
#include"arm_neon.h"

#define IMAGE_HEIGHT 480
#define IMAGE_WIDTH  640

float32_t integral_image[IMAGE_HEIGHT][IMAGE_WIDTH];

float32x4_t box_area_compute3(int, int , int , int , unsigned int , float);

inline int min(int, int);

int main()


 box_area_compute3(1, 1, 4, 4, 2, 0);

 return 0;


float32x4_t box_area_compute3(int row, int col, int num_rows, int num_cols, unsigned int step_size, float three)

 unsigned int height = IMAGE_HEIGHT;
 unsigned int width = IMAGE_WIDTH;

 int temp_row = row + num_rows;
 int temp_col = col + num_cols;

 int r1 = (min(row, height))- 1 ;
 int r2 = (min(temp_row, height)) - 1;

 int c1 = (min(col, width)) - 1;
 int c2 = (min(temp_col, width)) - 1;

 float32x4_t v128_areas;

 if(step_size == 2)
 
  float32x4x2_t top_left, top_right, bottom_left, bottom_right;
  top_left    = vld2q_f32((float32_t *)integral_image[r1] + c1);
  top_right   = vld2q_f32((float32_t *)integral_image[r1] + c2);
  bottom_left  = vld2q_f32((float32_t *)integral_image[r2] + c1);
  bottom_right  = vld2q_f32((float32_t *)integral_image[r2] + c2);

  v128_areas = vsubq_f32(vsubq_f32(vaddq_f32(top_left.val[0], bottom_right.val[0]), top_right.val[0]), bottom_left.val[0]);


 
 else if(step_size == 4)
 
  float32x4x4_t top_left, top_right, bottom_left, bottom_right;
  top_left   = vld4q_f32((float32_t *)integral_image[r1] + c1);
  top_right   = vld4q_f32((float32_t *)integral_image[r1] + c2);
  bottom_left  = vld4q_f32((float32_t *)integral_image[r2] + c1);
  bottom_right  = vld4q_f32((float32_t *)integral_image[r2] + c2);

  v128_areas = vsubq_f32(vsubq_f32(vaddq_f32(top_left.val[0], bottom_right.val[0]), top_right.val[0]), bottom_left.val[0]);

 

 if(three == 3.0)
  v128_areas = vmulq_n_f32(v128_areas, three);

 return v128_areas;



inline int min(int X, int Y)

 return (X < Y ? X : Y);


第 2 部分

arm-none-linux-gnueabi-gcc -O0 -g3 -Wall -c -fmessage-length=0 -fcommon -MMD -MP -MF"main.d" -MT"main.d" -mcpu=cortex-a8 -marm -mfloat-abi=hard -mfpu=neon-vfpv4 -o"main.o" "../main.c"

第 3 部分

../main.c: In function 'box_area_compute3':
../main.c:65: error: unable to find a register to spill in class 'GENERAL_REGS'
../main.c:65: error: this is the insn:
(insn 226 225 227 5 c:\program files\codesourcery\sourcery g++\bin\../lib/gcc/arm-none-linux-gnueabi/4.4.1/include/arm_neon.h:9863 (parallel [
           (set (reg:XI 148 [ D.17028 ])
               (unspec:XI [
                       (mem:XI (reg:SI 3 r3 [301]) [0 S64 A64])
                       (reg:XI 148 [ D.17028 ])
                       (unspec:V4SF [
                               (const_int 0 [0x0])
                           ] 191)
                   ] 111))
           (set (reg:SI 3 r3 [301])
               (plus:SI (reg:SI 3 r3 [301])
                   (const_int 32 [0x20])))
       ]) 1605 neon_vld4qav4sf (nil))
../main.c:65: confused by earlier errors, bailing out
cs-make: *** [main.o] Error 1

【问题讨论】:

是的,我觉得这与寄存器分配有关。当我使用 vld4q_f32(...) 时,需要分配 4 个寄存器,在该分配中,编译器可能会出现问题。 对我来说看起来像一个编译器错误。您可能应该报告它。 【参考方案1】:

我无法对此进行测试,因为我没有用于它的工具链,但这种类型的错误通常可以通过稍微改写代码来解决。通常这不应该发生,并且应该报告为错误,但您使用的是处理器特定功能,与编译器的其余部分相比,它可能没有经过良好的测试和完善。

由于这是一个寄存器溢出错误,并且您涉及多个指针,我高度怀疑编译器可能会尝试将更多数据加载到寄存器中,而不是担心可能会出现一些别名(可能实际上并没有发生)。下面我将处理这种可能性,并做一些其他的事情,从编译器的角度来看,这可能会降低代码的复杂性(尽管看起来可能并非如此)。

#include<stdio.h>
#include"arm_neon.h"

#define IMAGE_HEIGHT 480
#define IMAGE_WIDTH  640

float32_t integral_image[IMAGE_HEIGHT][IMAGE_WIDTH];

float32x4_t box_area_compute3(int, int , int , int , unsigned int , float);

inline int min(int, int);

int main()


 box_area_compute3(1, 1, 4, 4, 2, 0);

 return 0;


/* By putting these in separate functions the compiler will initially
 * think about them by themselves, without the complications of the
 * surrounding code.  This may give it the abiltiy to optimise the
 * code somewhat before trying to inline it.
 * This may also serve to make it more obvious to the compiler that
 * the local variables are dead after their use (since they are
 * dead after the call returns, and that the lifetimes of some variable
 * cannot actually overlap (hopefully reducing the register needs).
 */
static inline float32x4_t do_it2(float32_t *tl, float32_t *tr, float32_t *bl, float32_t * br) 
    float32x4x2_t top_left, top_right, bottom_left, bottom_right;
    float32x4_t A, B;

    top_left = vld2q_f32(tl);
    top_right = vld2q_f32(tr);
    bottom_left = vld2q_f32(bl);
    bottom_right = vld2q_f32(br);

    /* By spreading this across several statements I have created several
     * additional sequence points.  The compiler does not think that it
     * has to dereference all of the pointers before doing any of the
     * computations.... maybe. */
    A = vaddq_f32(*top_left.val, *bottom_right.val);
    B = vsubq_f32(A, *top_right.val);
    return vsubq_f32(B, *bottom_left);


static inline float32x4_t do_it4(float32_t *tl, float32_t *tr, float32_t *bl, float32_t * br) 
    float32x4x4_t top_left, top_right, bottom_left, bottom_right;
    float32x4_t A, B;

    top_left = vld4q_f32(tl);
    top_right = vld4q_f32(tr);
    bottom_left = vld4q_f32(bl);
    bottom_right = vld4q_f32(br);

    A = vaddq_f32(*top_left.val, *bottom_right.val);
    B = vsubq_f32(A, *top_right.val);
    return vsubq_f32(B, *bottom_left);


float32x4_t box_area_compute3(int row, int col, int num_rows, int num_cols, unsigned int step_size, float three)

 unsigned int height = IMAGE_HEIGHT;
 unsigned int width = IMAGE_WIDTH;

 int temp_row = row + num_rows;
 int temp_col = col + num_cols;

 int r1 = (min(row, height))- 1 ;
 int r2 = (min(temp_row, height)) - 1;

 int c1 = (min(col, width)) - 1;
 int c2 = (min(temp_col, width)) - 1;

 float32x4_t v128_areas;

     float32_t *tl = (float32_t *)integral_image[r1] + c1;
 float32_t *tr = (float32_t *)integral_image[r1] + c2;
 float32_t *bl = (float32_t *)integral_image[r2] + c1;
 float32_t *br = (float32_t *)integral_image[r2] + c2;


 switch (step_size) 
    case 2:
      v128_areas = do_it2(tl, tr, bl, br);
      break;

 case 4:
      v128_areas = do_it4(tl, tr, bl, br);
      break;
 

 if(three == 3.0)
  v128_areas = vmulq_n_f32(v128_areas, three);

 return v128_areas;



inline int min(int X, int Y)

 return (X < Y ? X : Y);

我希望这会有所帮助,并且我没有引入任何错误。

【讨论】:

感谢您的所有努力,但我很遗憾再次说,我得到了同样的错误:(。我和朋友的印象一样,我已经拆分了功能,但是该死的错误似乎并没有消失。这仅发生在第二部分(do_it4()),当我们仔细查看错误报告时,它巧妙地指向了neon_vld4qav4sf。:(我已将此报告给Code Sourcery,希望他们解决这个问题并发布补丁,很快,我真的没时间了!:) @vikramtheone:如果从命令行中删除-mfloat-abi=hard 会发生什么?我不知道这会对你的芯片产生什么影响。 我采取了不同的路线,我在汇编中编写了 do_it4(..) :),将它传递给汇编器生成了一个目标文件,将它传递给链接器,它运行良好。发生这个问题我感觉很好(顺便说一句,Codesourcery 要求我将优化标志从 -O3 降到 -O1,所以这是 gcc 中的一个错误),通过编写汇编和反汇编 obj 文件,我看到了我在更密集的代码中取得了相同的结果。是的,关于 -mfloat-abi=hard 的事情,我尝试了 soft 并且我仍然收到错误。我会尽快发布组装的东西作为答案。【参考方案2】:

好吧,我已经就这个问题联系了 Code Sourcery,他们认为这是 GCC 编译器中的一个错误。所以我在汇编中编写了 do_it4()..... 函数,而不是使用内在函数。现在效果很好!

【讨论】:

【参考方案3】:

行:

float32x4x4_t top_left, top_right, bottom_left, bottom_right;

使用所有 16 个 q 寄存器!编译器不能处理这个并不奇怪。您可能可以通过重写以使用更少的寄存器来解决此问题。

【讨论】:

【参考方案4】:

ARM NEON Cortex-A8 支持 vfpv3,Cortex-A5 支持 vfpv4 和 neon2,(至于:如果您使用 -mfloat-abi=hard,您将跳过在软件缺失指令中进行模拟的能力,因此您无法生成以下代码将针对 vfpv4 进行优化,但将在具有软件仿真的 vfpv3 上运行)

【讨论】:

以上是关于为 ARM NEON 编译时出现未知的 GCC 错误(严重)的主要内容,如果未能解决你的问题,请参考以下文章

我的数码相框.c文件用arm-linux-gcc交叉编译时出现错误,如tgzip.c:309:undefined reference to

ARM NEON 数据类型导致编译器崩溃

ARM NEON 没有 xor gcc 内在函数

GCC 向量扩展和 ARM NEON 的内存对齐问题

ARM 中的 NEON 实现

当我在 Linux 上运行使用硬浮点选项构建的 Neon 代码时出现分段错误