【问题标题】:Do compilers optimize out net zero bit shifts?编译器是否优化了净零位移?
【发布时间】:2018-07-24 08:25:12
【问题描述】:

我认为clang++ (Ubuntu clang version 3.5.2-3ubuntu1 (tags/RELEASE_352/final) (based on LLVM 3.5.2)) 正在编译的.cpp 文件中有一些类似于以下代码块的代码(我不允许发布原始代码)。
它看起来像C 代码,因为我们使用GoogleTest 来测试我们的C 代码。总之:

size_t const SHIFT = 4;
uint8_t var, var2;
/* Omitted: Code that sets var to, say 00011000 (base 2) */
var2 = var;
var = var << SHIFT >> SHIFT; // [1] result is 00011000 (base 2) (tested with printf)
var2 = var2 << SHIFT;
var2 = var2 >> SHIFT; // [2] result is 00001000 (base 2) (tested with printf)

现在,为什么评论 [1] 成立?我假设相应的行会导致前 4 位被清零。但我发现这不是真的;该程序只是恢复原始值。

这是某种语言定义的行为,还是 clang 编译出一个所谓无用的位移?

(我检查了关联性(使用this table on cppreference.com,假设基本运算符的关联性/优先级在C++ 的版本之间不会有所不同,并且可能不会在C++C 之间有所不同,至少在'current版本'),看起来[1] 处的 RHS 表达式确实应该产生与以下两个语句相同的结果)

【问题讨论】:

  • 整数提升。
  • 啊哈哈。谢谢@Mysticial!你愿意回答吗?否则我会很快自己回答。我想我的收获是宁愿使用&amp; 而不是左右移动......
  • 此外,通常左移后右移相同数量的位,这不是您似乎期望的身份操作。它执行掩码操作。
  • 如果 var 未初始化,则结果是未定义的行为,这意味着(除其他外)如果 var2 == var 是合法的,不会产生任何变化。
  • @polynomial_donut 相同的标志:clang 5.0.0(在 godbolt.org/g/kXVkQz 上测试):6:17:警告:隐式转换失去整数精度:'int' 到 'uint8_t'(又名' unsigned char') [-Wconversion]

标签: c++ c compilation compiler-optimization clang++


【解决方案1】:

您看到的是整数促销的结果。任何等级类型低于int 的值,在表达式中使用时,都会提升为int

这是C standard 的第 6.3.1.1 节中的详细信息

2 以下内容可用于intunsigned int 可能出现的表达式中 使用:

  • 具有整数类型的对象或表达式(intunsigned int 除外) 其整数转换等级小于或等于int 的等级并且 unsigned int
  • _Boolintsigned intunsigned int 类型的位字段。

如果int 可以表示原始类型的所有值(受宽度限制,对于 位域),该值被转换为int;否则,它将转换为unsigned int。这些称为整数促销。所有其他类型均由 整数促销。

在这种情况下:

var = var << SHIFT >> SHIFT;

var 首先被提升为int。这种类型至少有 16 位宽,最有可能是 32 位宽。所以被操作的值是0x00000018。左移 4 得到0x00000180,随后右移得到0x00000018

然后将结果存储在uint_8 中。由于该值适合这种类型的变量,因此不需要转换并且存储了0x18

【讨论】:

  • 我说var 被提升为size_t aka unsigned int(不是int)是不是错了?
  • @polynomial_donut 这是正确的,因为 1) size_t 可能大于 unsigned int,并且 2) 标准明确指出提升值的类型是int.
  • 如果您对语言律师的详细程度不感兴趣,经验法则是整数提升在 C 和 C++ 中很糟糕。如果您认为这很糟糕,请等到您使用整数类型(一般为数字类型)重载歧义。
  • @Mysticial 幸运的是,我们正在与C 合作,所以没有C++ voodoo :) (*基本单元测试代码除外-_-)
【解决方案2】:

表达式:

var = var << SHIFT >> SHIFT;

在语义上不等价于

var = var << SHIFT ;
var = var >> SHIFT ; 

这需要:

var = (uint8_t)(var << SHIFT) >> SHIFT ;

这说明了两者之间的有效区别 - 观察到的行为不需要优化,而是要求语言定义关于表达式中的类型提升规则。

也就是说,编译器也完全有可能优化移位。在这种情况下,优化可能不会改变定义行为的结果。

【讨论】:

    猜你喜欢
    • 2014-05-23
    • 2011-03-18
    • 1970-01-01
    • 2010-11-22
    • 2014-04-18
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    相关资源
    最近更新 更多