Это ошибка оптимизации в g ++? - PullRequest
6 голосов
/ 21 августа 2011

Я не уверен, нашел ли я ошибку в g ++ (4.4.1-4ubuntu9), или я делаю что-то не так.Я считаю, что я вижу ошибку, допущенную при включении оптимизации с помощью 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

А вот соответствующий код из 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

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);
  }
  ...
}

Если вы думаете, что я слишком много запутал или устранил, дайте мне знать.Пользователи этого кода называют «perfect_fine_invoker».С оптимизацией g ++ оптимизирует функцию has_any_actions, превращая ее в прямой вызов helper_function, который вы можете увидеть в сборке.

Вопрос

Итак, мой вопрос, так ли это?Выглядит как глючная оптимизация для кого-то еще?

Если бы это было полезно, я мог бы опубликовать продезинфицированную версию исходного кода C ++.

Это моя первая публикация в Stack Overflow, поэтому, пожалуйста,дайте мне знать, если я могу сделать что-нибудь, чтобы прояснить вопрос, или предоставить дополнительную информацию.

Ответ

Редактировать (через несколько дней после факта):

Iпринял ответ ниже на мой вопрос - это не было ошибкой оптимизации в g ++, я просто смотрел на код сборки неправильно.нашел ответ.Я немного читал о неопределенном поведении в C (http://blog.regehr.org/archives/213 и http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html), и некоторые описания компилятора, оптимизирующего функции с неопределенным поведением, показались мне знакомыми.

Я добавилнекоторые NULL-указатели проверяют функцию helper_function, и вот, ошибка ...Для начала мне следовало проверить NULL-указатель, но, видимо, не позволил g ++ сделать все, что он хотел (в моем случае, оптимизировать вызов).

Надеюсь, эта информация поможет кому-то в будущем.

1 Ответ

5 голосов
/ 21 августа 2011

Я думаю, что вы смотрите не на ту вещь. Я предполагаю, что компилятор заметил, что ваша функция короткая и не касается регистра %rdi, поэтому она просто оставляет его в покое (у вас та же переменная, что и у первого параметра, что, я полагаю, находится в %rdi. стр. 21 здесь http://www.x86 -64.org / документация / 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

При его оптимизации компилятор просто избавляется от всего, что движется туда-сюда.

...