这是g ++的优化错误吗?

我不确定是否在g ++(4.4.1-4ubuntu9)中发现了一个bug,或者如果我做错了什么。 我相信我看到的是通过使用g ++ -O2进行优化引入的错误。 我试图将代码提取到相关部分。

当启用优化时,我有一个失败的ASSERT。 优化禁用时,相同的ASSERT不会失败。 我想我已经追踪到一个函数及其调用者的优化。

系统

  • 语言:C ++
  • Ubuntu 9.10
  • g ++ – 4.4.real(Ubuntu 4.4.1-4ubuntu9)4.4.1
  • Linux 2.6.31-22-x86_64服务器

已启用优化

编译对象为: g++ -DHAVE_CONFIG_H -I. -fPIC -g -O2 -MT file.o -MD -MP -MF .deps/file.Tpo -c -o file.o file.cpp g++ -DHAVE_CONFIG_H -I. -fPIC -g -O2 -MT file.o -MD -MP -MF .deps/file.Tpo -c -o file.o file.cpp

这里是objdump -dg file.o的相关代码。

 00000000000018b0 <helper_function>: ;; This function takes two parameters: ;; pointer to int: %rdi ;; pointer to int[]: %rsi 18b0: 0f b6 07 movzbl (%rdi),%eax 18b3: 83 f8 12 cmp $0x12,%eax 18b6: 74 60 je 1918 <helper_function+0x68> 18b8: 83 f8 17 cmp $0x17,%eax 18bb: 74 5b je 1918 <helper_function+0x68> ... 1918: c7 06 32 00 00 00 movl $0x32,(%rsi) 191e: 66 90 xchg %ax,%ax 1920: c3 retq 0000000000005290 <buggy_invoker>: ... snip ... 52a0: 48 81 ec c8 01 00 00 sub $0x1c8,%rsp 52a7: 48 8d 84 24 a0 01 00 lea 0x1a0(%rsp),%rax 52ae: 00 52af: 48 c7 84 24 a0 01 00 movq $0x0,0x1a0(%rsp) 52b6: 00 00 00 00 00 52bb: 48 c7 84 24 a8 01 00 movq $0x0,0x1a8(%rsp) 52c2: 00 00 00 00 00 52c7: c7 84 24 b0 01 00 00 movl $0x0,0x1b0(%rsp) 52ce: 00 00 00 00 52d2: 4c 8d 7c 24 20 lea 0x20(%rsp),%r15 52d7: 48 89 c6 mov %rax,%rsi 52da: 48 89 44 24 08 mov %rax,0x8(%rsp) ;; ***** BUG HERE ***** ;; Pointer to int[] loaded into %rsi ;; But where is %rdi populated? 52df: e8 cc c5 ff ff callq 18b0 <helper_function> 0000000000005494 <perfectly_fine_invoker>: 5494: 48 83 ec 20 sub $0x20,%rsp 5498: 0f ae f0 mfence 549b: 48 8d 7c 24 30 lea 0x30(%rsp),%rdi 54a0: 48 89 e6 mov %rsp,%rsi 54a3: 48 c7 04 24 00 00 00 movq $0x0,(%rsp) 54aa: 00 54ab: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp) 54b2: 00 00 54b4: c7 44 24 10 00 00 00 movl $0x0,0x10(%rsp) 54bb: 00 ;; Non buggy invocation here: both %rdi and %rsi loaded correctly. 54bc: e8 ef c3 ff ff callq 18b0 <helper_function> 

优化禁用

现在编译为: g++ -DHAVE_CONFIG_H -I. -fPIC -g -O0 -MT file.o -MD -MP -MF .deps/file.Tpo -c -o file.o file.cpp g++ -DHAVE_CONFIG_H -I. -fPIC -g -O0 -MT file.o -MD -MP -MF .deps/file.Tpo -c -o file.o file.cpp

 0000000000008d27 <helper_function>: ;; Still the same parameters here, but it looks a little different. ... snip ... 8d2b: 48 89 7d e8 mov %rdi,-0x18(%rbp) 8d2f: 48 89 75 e0 mov %rsi,-0x20(%rbp) 8d33: 48 8b 45 e8 mov -0x18(%rbp),%rax 8d37: 0f b6 00 movzbl (%rax),%eax 8d3a: 0f b6 c0 movzbl %al,%eax 8d3d: 89 45 fc mov %eax,-0x4(%rbp) 8d40: 8b 45 fc mov -0x4(%rbp),%eax 8d43: 83 f8 17 cmp $0x17,%eax 8d46: 74 40 je 8d88 <helper_function+0x61> ... 000000000000948a <buggy_invoker>: 948a: 55 push %rbp 948b: 48 89 e5 mov %rsp,%rbp 948e: 41 54 push %r12 9490: 53 push %rbx 9491: 48 81 ec c0 01 00 00 sub $0x1c0,%rsp 9498: 48 89 bd 38 fe ff ff mov %rdi,-0x1c8(%rbp) 949f: 48 89 b5 30 fe ff ff mov %rsi,-0x1d0(%rbp) 94a6: 48 c7 45 c0 00 00 00 movq $0x0,-0x40(%rbp) 94ad: 00 94ae: 48 c7 45 c8 00 00 00 movq $0x0,-0x38(%rbp) 94b5: 00 94b6: c7 45 d0 00 00 00 00 movl $0x0,-0x30(%rbp) 94bd: 48 8d 55 c0 lea -0x40(%rbp),%rdx 94c1: 48 8b 85 38 fe ff ff mov -0x1c8(%rbp),%rax 94c8: 48 89 d6 mov %rdx,%rsi 94cb: 48 89 c7 mov %rax,%rdi ;; ***** NOT BUGGY HERE ***** ;; Now, without optimization, both %rdi and %rsi loaded correctly. 94ce: e8 54 f8 ff ff callq 8d27 <helper_function> 0000000000008eec <different_perfectly_fine_invoker>: 8eec: 55 push %rbp 8eed: 48 89 e5 mov %rsp,%rbp 8ef0: 48 83 ec 30 sub $0x30,%rsp 8ef4: 48 89 7d d8 mov %rdi,-0x28(%rbp) 8ef8: 48 c7 45 e0 00 00 00 movq $0x0,-0x20(%rbp) 8eff: 00 8f00: 48 c7 45 e8 00 00 00 movq $0x0,-0x18(%rbp) 8f07: 00 8f08: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%rbp) 8f0f: 48 8d 55 e0 lea -0x20(%rbp),%rdx 8f13: 48 8b 45 d8 mov -0x28(%rbp),%rax 8f17: 48 89 d6 mov %rdx,%rsi 8f1a: 48 89 c7 mov %rax,%rdi ;; Another example of non-optimized call to that function. 8f1d: e8 05 fe ff ff callq 8d27 <helper_function> 

原始的C ++代码

这是原始C ++的消毒版本。 我只是改了一些名字,并删除了不相关的代码。 原谅我的偏执狂,我只是不想从未发表和未发表的作品中暴露过多的代码:-)。

 static void helper_function(my_struct_t *e, int *outArr) { unsigned char event_type = e->header.type; if (event_type == event_A || event_type == event_B) { outArr[0] = action_one; } else if (event_type == event_C) { outArr[0] = action_one; outArr[1] = action_two; } else if (...) { ... } } static void buggy_invoker(my_struct_t *e, predicate_t pred) { // MAX_ACTIONS is #defined to 5 int action_array[MAX_ACTIONS] = {0}; helper_function(e, action_array); ... } static int has_any_actions(my_struct_t *e) { int actions[MAX_ACTIONS] = {0}; helper_function(e, actions); return actions[0] != 0; } // *** ENTRY POINT to this code is this function (note not static). void perfectly_fine_invoker(my_struct_t e, predicate_t pred) { memfence(); if (has_any_actions(&e)) { buggy_invoker(&e, pred); } ... } 

如果你认为我已经混淆或消除太多,让我知道。 这段代码的用户调用“perfectly_fine_invoker”。 通过优化,g ++优化了“has_any_actions”function,直接调用“helper_function”,您可以在程序集中看到这个function。

问题

所以,我的问题是,它看起来像一个错误的优化给其他人吗?

如果这会有帮助的话,我可以发布原始C ++代码的消毒版本。

这是我第一次发布到堆栈溢出,所以请让我知道,如果我可以做任何事情,使问题更清楚,或提供任何额外的信息。

答案

编辑(几天之后):

我接受了我的问题下面的答案 – 这不是g ++中的优化错误,我只是看着汇编代码错误。

但是,对于将来可能会看到这个问题的人,我已经find了答案。 我做了一些关于C中未定义行为的阅读( http://blog.regehr.org/archives/213和http://blog.llvm.org/2011/05/what-every-c-programmer-should-know。 HTML )和一些对未定义行为优化function的编译器的描述看起来非常熟悉。

我添加了一些NULL指针检查函数'helper_function'和罗,看哪…错误消失。 我应该有NULL指针检查开始,但显然没有让他们允许g ++做任何想做的事情(在我的情况下,优化了通话)。

希望这些信息可以帮助某个人。

我认为你正在看错的东西。 我想编译器会注意到你的函数很短,并且不会触及%rdi寄存器,所以它只是让它保持不变(你有和第一个参数相同的变量,我猜是放在%rdi 。这里http://www.x86-64.org/documentation/abi.pdf

如果您查看未优化版本,则会保存此行上的%rdi寄存器

 9498: 48 89 bd 38 fe ff ff mov %rdi,-0x1c8(%rbp) 

…然后在调用helper_function之前,它会将保存的值移入%rax ,并将其移入%rdi

 94c1: 48 8b 85 38 fe ff ff mov -0x1c8(%rbp),%rax 94c8: 48 89 d6 mov %rdx,%rsi 94cb: 48 89 c7 mov %rax,%rdi 

编译器在优化时只是摆脱所有来回移动。