В расширенном встроенном ассемблере в стиле GCC можно вывести «виртуализированное» логическое значение, например, несущий флаг? - PullRequest
5 голосов
/ 20 февраля 2010

Если у меня есть следующий код C ++ для сравнения двух 128-разрядных целых чисел без знака со встроенным asm-64 amd:

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

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

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

Ответы [ 2 ]

4 голосов
/ 16 января 2017

Здесь мы почти 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.

4 голосов
/ 21 февраля 2010

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

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
...