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。
comments powered by Disqus