Theme Preview

ARM编译器ADS1.2 Build 848存在乱序优化Bug

由 李晓岚 在 2011年05月27日发表

乱序优化的关键在于确定哪些指令可以乱序执行,而不会最终影响执行的结果。一旦在判断哪些指令能够乱序执行的问题上出现误判,就回导致Bug的引入。

ARM编译器ADS 1.2 Build 848的Thumb指令编译程序TCC.EXE,在使用优化选项-O2时,编译器就会进行乱序优化。而在某些特定情况下,TCC会在判断可以乱序执行的指令上出现误判,导致Bug的出现。

下面这段简单的C程序,就能诱使TCC优化出错。

 

typedef struct{
  int x;
  int y;
}Point;

typedef struct{
  Point pt;
}Object;

extern void variadic_function( int, ... );

int out_of_order_optimizing_bug( Object* obj, int x ){
  variadic_function( 0 );
  if( obj->pt.x - x > 0 ){/* 这个判断引入Bug */
    return obj->pt.y - 1;
  }else{
    return obj->pt.y - 2;
  }
}

使用TCC编译器,编译选项为:-O2 -S,生成汇编指令如下:

 

; generated by Thumb C Compiler, ADS1.2 [Build 848]

; commandline [-O2 -S "-IC:\Program Files\ARM\ADSv1_2\INCLUDE"]
        CODE16

        AREA ||.text||, CODE, READONLY

out_of_order_optimizing_bug PROC
        PUSH     {r4,r5,r7,lr}
        MOV      r4,r0
        MOV      r5,r1
        MOV      r0,#0
        BL       variadic_function
        LDR      r0,[r4,#0]
        SUB      r0,r0,r5
        LDR      r0,[r4,#4]
        CMP      r0,#0
        BLE      |L1.26|
        SUB      r0,#1
        POP      {r4,r5,r7,pc}
|L1.26|
        SUB      r0,#2
        POP      {r4,r5,r7,pc}
        DCW      0000
        ENDP

其中,15行SUB r0,r0,r5进行的是obj->pt.x - x运算,结果保存在r0寄存器,16行LDR R0,[R4,#4]是将obj->pt.y载入寄存器r0。17行CMP r0,#0将r0和0进行比较,原意是判断obj->pt.x - x > 0的结果,根据比较的结果进行分支跳转,由于16行也使用寄存器r0装载了obj->pt.y的值,所以比较的结果已经不是原来程序的本意了,于是Bug就华丽丽的产生了。

要出现上述问题的一个必要条件就是,出现问题的那条if语句的两个分支都需要装载ojb->pt.y的值进行运算,编译器聪明地看到了这点,于是将公共指令提前到了判断之前。如果将obj->pt.y的值装载到r0以为的其他寄存器,这就是一个很好的优化,只可惜……

比较语句前的那个变参函数variadic_function调用也是一个诱因,但不是必要条件。如果去掉这个变参函数调用,编译器就会选择r0以外的寄存器来加载obj->pt.y的值。目前还没发现其他情况能够导致编译器错误选择了r0寄存器。

结论:尽量不要在不同条件分支存在公共表达式的语句,这样可以消除TCC乱序优化Bug的必要条件。ADS编译器版本过于古老,维护更新停滞,建议使用最新版本的RVCT编译器,避免不必要编译器Bug。

标签:ARMADSTCC

comments powered by Disqus