Как я могу получить G CC, чтобы оптимизировать эту команду сдвига битов в ход? - PullRequest
6 голосов
/ 07 марта 2020

Я пытаюсь использовать следующий код для эмуляции 16-разрядного полуплавающего в программном обеспечении:

typedef struct half
{
    unsigned short mantissa:10;
    unsigned short exponent:5;
    unsigned short sign:1;
} half;

unsigned short from_half(half h)
{
    return h.mantissa | h.exponent << 10 | h.sign << 15;
}

half to_half(unsigned short s)
{
    half result = { s, s >> 10, s >> 15 };
    return result;
}

Я настроил это так, чтобы его можно было легко оптимизировать в инструкцию перемещения, но и вот, в from_half G CC все равно выполняет сдвиг битов (даже при -O3):

from_half:
        mov     edx, edi
        mov     eax, edi
        and     di, 1023
        shr     dx, 15
        and     eax, 31744
        movzx   edx, dl
        sal     edx, 15
        or      eax, edx
        or      eax, edi
        ret

, в то время как to_half прекрасно оптимизируется:

to_half:
        mov     eax, edi
        ret

Godbolt

Я пробовал разные уровни оптимизации (-O1, -O2, -Os), но никто не оптимизировал его до того, на что я надеялся.

Clang делает это так, как я ожидал, даже на -O1:

from_half:                              # @from_half
        mov     eax, edi
        ret
to_half:                                # @to_half
        mov     eax, edi
        ret

Godbolt

Как я могу получить G CC, чтобы оптимизировать это в шаг? Почему он уже не оптимизирован таким образом?

Ответы [ 2 ]

6 голосов
/ 07 марта 2020

В дополнение к ответу Booboo , вы можете попробовать следующее, что отвечает на ваш вопрос

Как я могу получить G CC, чтобы оптимизировать это в ход?

Просто приведите каждое сдвинутое выражение битового поля к unsigned short

unsigned short from_half(half h)
{
    return (unsigned short)h.mantissa | (unsigned short)(h.exponent << 10) | (unsigned short)(h.sign << 15);
}

https://godbolt.org/z/CfZSgC

В результате:

from_half:
        mov     eax, edi
        ret

Почему это уже не оптимизировано таким образом?

Я не уверен, что у меня есть solid ответ на этот вопрос. Очевидно, что промежуточное повышение битовых полей до int сбивает с толку оптимизатор ... Но это всего лишь предположение.

2 голосов
/ 07 марта 2020

Прошло много времени с тех пор, как я кодировал C, но, похоже, использование union должно работать:

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

static bool useUnion;

__attribute__ ((__constructor__)) // supported by gcc compiler
static void initUseUnion()
{
    union {
       uint16_t i;
       char c[2];
    } n = { 0x0001 };
    useUnion = n.c[0]; // little endian
}

typedef struct half
{
    unsigned short mantissa:10;
    unsigned short exponent:5;
    unsigned short sign:1;
} half;

typedef union half_short
{
    half h;
    uint16_t s;
} half_short;

unsigned short from_half(half h)
{
    if (useUnion) {
        half_short hs;
        hs.h = h;
        return hs.s;
    }
    else {
        return h.mantissa | h.exponent << 10 | h.sign << 15;
    }
}

half to_half(unsigned short s)
{
    if (useUnion) {
        half_short hs;
        hs.s = s;
        return hs.h;
    }
    else {
        half result = { s, s >> 10, s >> 15 };
        return result;
    }
}

int main(int argc, char* argv[])
{
    printf("%d\n", useUnion);
    return 0;
}
...