Возможно ли в расширенном встроенном ассемблере в стиле 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
Другие вопросы по тегам