Возможно ли в расширенном встроенном ассемблере в стиле GCC вывести "виртуализированное" логическое значение, например, флаг переноса?
Если у меня есть следующий код C++ для сравнения двух 128-разрядных целых чисел без знака, с встроенным amd-64 asm:
struct uint128_t {
uint64_t lo, hi;
};
inline bool operator< (const uint128_t &a, const uint128_t &b)
{
uint64_t temp;
bool result;
__asm__(
"cmpq %3, %2;"
"sbbq %4, %1;"
"setc %0;"
: // outputs:
/*0*/"=r,1,2"(result),
/*1*/"=r,r,r"(temp)
: // inputs:
/*2*/"r,r,r"(a.lo),
/*3*/"emr,emr,emr"(b.lo),
/*4*/"emr,emr,emr"(b.hi),
"1"(a.hi));
return result;
}
Тогда он будет вставлен довольно эффективно, но с одним недостатком. Возвращаемое значение осуществляется через "интерфейс" общего регистра со значением 0 или 1. Это добавляет две или три ненужных дополнительных инструкции и отвлекает от операции сравнения, которая в противном случае была бы полностью оптимизирована. Сгенерированный код будет выглядеть примерно так:
mov r10, [r14]
mov r11, [r14+8]
cmp r10, [r15]
sbb r11, [r15+8]
setc al
movzx eax, al
test eax, eax
jnz is_lessthan
Если я использую "sbb %0,%0" с возвращаемым значением "int" вместо "setc %0" с возвращаемым значением "bool", есть еще две дополнительные инструкции:
mov r10, [r14]
mov r11, [r14+8]
cmp r10, [r15]
sbb r11, [r15+8]
sbb eax, eax
test eax, eax
jnz is_lessthan
Что я хочу это:
mov r10, [r14]
mov r11, [r14+8]
cmp r10, [r15]
sbb r11, [r15+8]
jc is_lessthan
GCC расширенный встроенный Asm замечательно, в противном случае. Но я хочу, чтобы это было так же хорошо, как встроенная функция, во всех отношениях. Я хочу иметь возможность напрямую возвращать логическое значение в форме состояния флага или флагов ЦП без необходимости "рендеринга" его в общий регистр.
Возможно ли это, или GCC (и компилятор Intel C++, который также позволяет использовать эту форму встроенного ассемблера) придется изменить или даже изменить, чтобы сделать это возможным?
Кроме того, пока я занимаюсь этим - можно ли как-нибудь улучшить мою формулировку оператора сравнения?
2 ответа
Здесь мы почти 7 лет спустя, и ДА, gcc наконец-то добавил поддержку "флагов вывода" (добавлено в 6.1.0, выпущено в апреле 2016 года). Подробные документы здесь, но вкратце это выглядит так:
/* Test if bit 0 is set in 'value' */
char a;
asm("bt $0, %1"
: "=@ccc" (a)
: "r" (value) );
if (a)
blah;
Чтобы понять =@ccc
: Выходное ограничение (которое требует =
) имеет тип @cc
с последующим кодом условия для использования (в этом случае c
для ссылки на флаг переноса).
Хорошо, это может больше не быть проблемой для вашего конкретного случая (поскольку gcc теперь поддерживает непосредственное сравнение 128-битных типов данных), но (в настоящее время) 1326 человек просмотрели этот вопрос. Видимо, есть некоторая заинтересованность в этой функции.
Теперь я лично поддерживаю школу мысли, которая говорит, что не используйте встроенный ассемблер вообще. Но если нужно, да, вы можете (сейчас) "вывести" флаги.
FWIW.
Я не знаю, как это сделать. Вы можете или не можете считать это улучшением:
inline bool operator< (const uint128_t &a, const uint128_t &b)
{
register uint64_t temp = a.hi;
__asm__(
"cmpq %2, %1;"
"sbbq $0, %0;"
: // outputs:
/*0*/"=r"(temp)
: // inputs:
/*1*/"r"(a.lo),
/*2*/"mr"(b.lo),
"0"(temp));
return temp < b.hi;
}
Это производит что-то вроде:
mov rdx, [r14]
mov rax, [r14+8]
cmp rdx, [r15]
sbb rax, 0
cmp rax, [r15+8]
jc is_lessthan