ARM GCC 在优化期间删除所需的代码

ARM GCC removing required code during optimization

我有以下代码,它执行从 16bpp 图像到 1bpp 图像的真正基本转换,代码按预期运行,直到我启用编译器优化,此时我只得到一个黑色图像。

#define RSCALE 5014709
#define GSCALE 9848225
#define BSCALE 1912602

uint16_t _convertBufferTo1bit(uint8_t* buffer, uint16_t size)
{
    uint8_t* dst_ptr = buffer;
    uint8_t* end_ptr = buffer + size;
    uint16_t pos = 0;
    uint8_t r, g, b, i;
    uint32_t lum;

    while(buffer < end_ptr)
    {
        for(i = 8; i > 0; i--)
        {
            r = (*buffer & 0xF8);
            g = ((*buffer & 0x07) << 5);
            buffer += 1;
            g |= (*buffer & 0x03);
            b = ((*buffer & 0x1F) << 3);
            buffer += 1;

            lum = ((RSCALE * r) + (GSCALE * g) + (BSCALE * b));
            if(lum > 0x7FFFFFFF)
            {
                //White
                dst_ptr[pos] |= (1 << (i-1));
            }
            else
            {
                //black
                dst_ptr[pos] &= ~(1 << (i-1));
            }
        }
        pos++;
    }

    return pos;
}

查看反编译的程序集时,我可以看到 if(lum > 0x7FFFFFFF) 语句和所有关联的计算已被编译器删除。谁能帮我理解为什么?

-O0 -std=c++17 -Wall -Wextra

https://godbolt.org/z/GhPezzh33

-O1 -std=c++17 -Wall -Wextra

https://godbolt.org/z/bn1M4319h

在此代码中:

lum = ((RSCALE * r) + (GSCALE * g) + (BSCALE * b));
if(lum > 0x7FFFFFFF)

RSCALEGSCALEBSCALE分别为501470998482251912602。假设 int 在正在使用的 C 实现中是 32 位,这些都是 int 常量。

rgb都是uint8_t类型,所以在乘法运算中提升为int

那么 ((RSCALE * r) + (GSCALE * g) + (BSCALE * b)) 是一个完全由 int 个操作数产生 int 结果的计算。因此编译器可以看到 lum 被赋予了 int 结果的值,并且有权假定结果在 INT_MININT_MAX 范围内。此外,它可以看到所有操作数都是非负的,因此不可能出现负结果,从而将可能的范围缩小到 0INT_MAX。这排除了将负值分配给 uint32_t 会导致换行到高值的可能性。所以编译器可能会假设 lum > 0x7FFFFFFF 永远不会为真。

计算可能会溢出int,然后行为未定义,编译器仍然允许使用假设。

要更正此问题,请将每个乘法的至少一个操作数更改为 unsigned