В моем исследовательском проекте я пишу код 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) g++ -O3
:
f1(unsigned int, unsigned int):
add %edi,%esi
mov %esi,%eax
adc $0x0,%eax
retq
Для всех значений X
которые я тестировал, код такой же, как указано выше (за исключением, конечно, непосредственного значения $0x0
которое изменяется соответственно). Однако я обнаружил одно исключение: когда X == -1
(или 0xFFFFFFFFu
или ~0u
,... на самом деле не имеет значения, как вы его ~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 $0x0,%eax
retq
Для X == -1
я несчастен :-(
f2(unsigned int, unsigned int):
add %esi,%edi
mov $0xffffffff,%eax
setb %dl
add $0xff,%dl
adc %edi,%eax
retq
Я получаю ADC
но это явно не самый эффективный код. (Что dl
там делает? Две инструкции, чтобы прочитать флаг переноса и восстановить его? В самом деле? Я надеюсь, что я очень плохо!)