В расширенном встроенном asm в стиле 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

В остальном расширенный встроенный asm GCC - это замечательно. Но я хочу, чтобы она была такой же хорошей, как внутренняя функция, во всех смыслах. Я хочу иметь возможность напрямую возвращать логическое значение в виде состояния флага или флагов ЦП, без необходимости «отображать» его в общем регистре.

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

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


person Deadcode    schedule 20.02.2010    source источник
comment
По состоянию на 2013 год, все еще не представляется возможным делать напрямую. Но вот отчет об ошибке за 2011 год, в котором обсуждается желательность такой функции: gcc.gnu. org / bugzilla / show_bug.cgi? id = 49611. Он ссылается на ветку ядра Linux 2001 года, которая также желает такой вещи: lkml .indiana.edu / hypermail / linux / kernel / 0111.2 / 0256.html.   -  person Nathan Kurz    schedule 24.10.2013


Ответы (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 человек просмотрели этот вопрос. Видимо есть интерес к этой функции.

Теперь я лично поддерживаю точку зрения, согласно которой вообще не используйте встроенный asm. Но если вы должны, да, вы можете (сейчас) «выводить» флаги.

FWIW.

person David Wohlferd    schedule 16.01.2017

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

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
person andrewffff    schedule 21.02.2010
comment
+1 Отличное улучшение. Этот тест лучше (для выполнения операции сортировки нескольких гигабайт), чем то, что я опубликовал в вопросе. Я все еще надеюсь, что может быть способ вернуть флаг переноса напрямую, поэтому я пока не буду отмечать это как принятый ответ, но все сделано хорошо. - person Deadcode; 22.02.2010