2010-02-20 4 views
5

Если у меня есть следующий код C++ для сравнения двух 128-разрядных целых чисел без знака, с инлайн АМД-64 ассемблере:В расширенном встроенном asm в стиле GCC можно вывести «виртуализированное» логическое значение, например. флаг переноса?

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» с «ИНТ» возвращаемого значения вместо «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 расширенный рядный асмовый замечательно, в противном случае. Но я хочу, чтобы это было так же хорошо, как и внутренняя функция. Я хочу иметь возможность напрямую возвращать логическое значение в виде состояния флага или флагов ЦП без необходимости «рендерить» его в общий регистр.

Возможно ли это, или же GCC (и компилятор Intel C++, который также позволяет использовать эту форму встроенного asm), необходимо изменить или даже реорганизовать, чтобы сделать возможным?

Кроме того, пока я нахожусь в нем - есть ли другой способ, который может улучшить моя формулировка оператора сравнения?

+0

Начиная с 2013 года, по-видимому, это невозможно сделать напрямую. Но вот отчет об ошибке 2011 года, в котором обсуждается желательность такой функции: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49611. Он ссылается на поток ядра Linux 2001 года, который также хочет этого: http: //lkml.indiana.Edu/hypermail/Linux/ядро ​​/ 0111,2/0256.html. –

ответ

3

Здесь мы почти 7 лет спустя, и YES, НКУ наконец добавлена ​​поддержка для «вывода флагов» (добавлено в 6.1.0, выпущено ~ апрель 2016 года). Подробные документы являются here, но в общем, это выглядит следующим образом:

/* Test if bit 0 is set in 'value' */ 
char a; 

asm("bt $0, %1" 
    : "[email protected]" (a) 
    : "r" (value)); 

if (a) 
    blah; 

Чтобы понять [email protected]: Выходное ограничение (что требует =) типа @cc с последующим кодом условия использования (в данном случае c для ссылки на флаг переноса).

Хорошо, это может быть не проблемой для вашего конкретного случая больше (поскольку gcc теперь поддерживает сравнение 128-битных типов данных напрямую), но (в настоящее время) 1 326 человек просмотрели этот вопрос. Видимо, в этом есть интерес.

Теперь я лично предпочитаю школу мысли, которая говорит don't use inline asm at all. Но если вы должны, да, вы можете (сейчас) 'выводить' флаги.

FWIW.

4

Я не знаю, как это сделать. Вы можете или не может считать это улучшение:

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 
+0

+1 Отличное улучшение. Это лучше работает (для выполнения операции сортировки нескольких гигабайт), чем то, что я разместил в вопросе. Я все еще надеюсь, что может быть способ вернуть флаг переноса напрямую, поэтому я не буду отмечать это как «принятый ответ», но, красиво сделанный. – Deadcode