我如何获得GCC来将这条移位指令优化为移动?

问题描述 投票:2回答:2

我正在尝试使用以下代码在软件中模拟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中,GCC还是进行了位移位(即使在-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

我如何获得GCC来对此进行优化?为什么还没有这样优化呢?

c gcc compiler-optimization bit-fields
2个回答
1
投票

自从我用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;
    }
}

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

0
投票

除了Booboo's答案,您还可以尝试以下回答您问题的方法>>

我如何获得GCC来优化此举?

只需将每个移位的位字段表达式强制转换为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

为什么没有这样优化呢?

我不确定对此是否有可靠的答案。显然,将位字段中间提升为int会使优化器感到困惑...但这只是一个猜测。

© www.soinside.com 2019 - 2024. All rights reserved.