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
在此代码中:
lum = ((RSCALE * r) + (GSCALE * g) + (BSCALE * b));
if(lum > 0x7FFFFFFF)
RSCALE
、GSCALE
、BSCALE
分别为5014709
、9848225
、1912602
。假设 int
在正在使用的 C 实现中是 32 位,这些都是 int
常量。
r
、g
、b
都是uint8_t
类型,所以在乘法运算中提升为int
。
那么 ((RSCALE * r) + (GSCALE * g) + (BSCALE * b))
是一个完全由 int
个操作数产生 int
结果的计算。因此编译器可以看到 lum
被赋予了 int
结果的值,并且有权假定结果在 INT_MIN
到 INT_MAX
范围内。此外,它可以看到所有操作数都是非负的,因此不可能出现负结果,从而将可能的范围缩小到 0
到 INT_MAX
。这排除了将负值分配给 uint32_t
会导致换行到高值的可能性。所以编译器可能会假设 lum > 0x7FFFFFFF
永远不会为真。
计算可能会溢出int
,然后行为未定义,编译器仍然允许使用假设。
要更正此问题,请将每个乘法的至少一个操作数更改为 unsigned
。
我有以下代码,它执行从 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
在此代码中:
lum = ((RSCALE * r) + (GSCALE * g) + (BSCALE * b));
if(lum > 0x7FFFFFFF)
RSCALE
、GSCALE
、BSCALE
分别为5014709
、9848225
、1912602
。假设 int
在正在使用的 C 实现中是 32 位,这些都是 int
常量。
r
、g
、b
都是uint8_t
类型,所以在乘法运算中提升为int
。
那么 ((RSCALE * r) + (GSCALE * g) + (BSCALE * b))
是一个完全由 int
个操作数产生 int
结果的计算。因此编译器可以看到 lum
被赋予了 int
结果的值,并且有权假定结果在 INT_MIN
到 INT_MAX
范围内。此外,它可以看到所有操作数都是非负的,因此不可能出现负结果,从而将可能的范围缩小到 0
到 INT_MAX
。这排除了将负值分配给 uint32_t
会导致换行到高值的可能性。所以编译器可能会假设 lum > 0x7FFFFFFF
永远不会为真。
计算可能会溢出int
,然后行为未定义,编译器仍然允许使用假设。
要更正此问题,请将每个乘法的至少一个操作数更改为 unsigned
。