Есть ли что-то особенное в -1 (0xFFFFFFFF) в отношении АЦП?

В моем исследовательском проекте я пишу код на C ++. Тем не менее, сгенерированная сборка является одним из ключевых моментов проекта. C ++ не обеспечивает прямой доступ к инструкциям по манипулированию флагами, в частности, к ADC, но это не должно быть проблемой, если компилятор достаточно умен, чтобы его использовать. Рассмотрим:

constexpr unsigned X = 0;

unsigned f1(unsigned a, unsigned b) {
    b += a;
    unsigned c = b < a;
    return c + b + X;
}

Переменная c - это обходной путь, чтобы взять меня в руки флаг переноса и добавить его в b и X. Похоже, мне повезло, и сгенерированный код (g++ -O3, версия 9.1) выглядит так:

f1(unsigned int, unsigned int):
 add %edi,%esi
 mov %esi,%eax
 adc [111]x0,%eax
 retq 

Для всех значений X, которые я тестировал, код такой же, как указано выше (за исключением Конечно для непосредственного значения [1112]x0, которое изменяется соответственно). Однако я обнаружил одно исключение: когда X == -1 (или 0xFFFFFFFFu или ~0u, ... это действительно не имеет значения, как вы пишете это), сгенерированный код выглядит так:

f1(unsigned int, unsigned int):
 xor %eax,%eax
 add %edi,%esi
 setb %al
 lea -0x1(%rsi,%rax,1),%eax
 retq 

Это кажется менее эффективен, чем исходный код, как это было предложено косвенными измерениями (хотя и не очень научно) Прав ли я? Если да, то это ошибка типа «отсутствующей возможности оптимизации», о которой стоит сообщить?

Для чего стоит, clang -O3, версия 8.8.0, всегда использует ADC (как я хотел) и icc -O3, версия 19.0.1 никогда не делает.

Я пытался использовать встроенный _addcarry_u32, но это не помогло.

unsigned f2(unsigned a, unsigned b) {
    b += a;
    unsigned char c = b < a;
    _addcarry_u32(c, b, X, &b);
    return b;
}

Я считаю, что я не правильно использовал _addcarry_u32 (я не мог найти много информации об этом). Какой смысл его использовать, так как я должен предоставить флаг переноса? (Снова, представляя c и молясь, чтобы компилятор понимал ситуацию.)

Я мог бы, на самом деле, использовать его правильно. Для X == 0 я счастлив:

f2(unsigned int, unsigned int):
 add %esi,%edi
 mov %edi,%eax
 adc [114]x0,%eax
 retq 

Для X == -1 я несчастлив: - (

f2(unsigned int, unsigned int):
 add %esi,%edi
 mov [115]xffffffff,%eax
 setb %dl
 add [115]xff,%dl
 adc %edi,%eax
 retq 

Я получаю ADC, но это явно не самый эффективный код. (Что там делает dl? Две инструкции прочитать флаг переноса и восстановить его? Правда? Надеюсь, я очень ошибаюсь!)

38
задан Cassio Neri 12 July 2019 в 14:34
поделиться