在 C 中,如果 B 是易失性的,则表达式 (void)(B = 1) 应该读取 B
我负责几个嵌入式平台的编译器。一位用户最近抱怨我们的一个编译器出现以下行为。给定如下代码:
extern volatile int MY_REGISTER;
void Test(void)
{
(void) (MY_REGISTER = 1);
}
编译器生成此代码(在伪汇编程序中):
Test:
move regA, 1
store regA, MY_REGISTER
load regB, MY_REGISER
也就是说,它不仅写入 MY_REGISTER,而且随后将其读回。由于性能原因,额外的负载让他感到不安。我解释说这是因为根据标准“赋值表达式具有赋值后左操作数的值,[...]”。
奇怪的是,移除cast-to-void会改变行为:负载消失。用户很高兴,但我只是感到困惑。
所以我还在 GCC 的几个版本(3.3 和 4.4)中检查了这一点。在那里,编译器永远不会生成负载,即使显式使用该值,例如
int TestTwo(void)
{
return (MY_REGISTER = 1);
}
变成
TestTwo:
move regA, 1
store regA, MY_REGISTER
move returnValue, 1
return
是否有人认为哪个是标准的正确解释?是否应该进行回读?如果使用该值或将其强制转换为 void,则添加只读是否正确或有用?
I work on compilers for a couple of embedded platforms. A user has recently complained about the following behaviour from one of our compilers. Given code like this:
extern volatile int MY_REGISTER;
void Test(void)
{
(void) (MY_REGISTER = 1);
}
The compiler generates this (in pseudo-assembler):
Test:
move regA, 1
store regA, MY_REGISTER
load regB, MY_REGISER
That is, it not only writes to MY_REGISTER, but reads it back afterwards. The extra load upset him for performance reasons. I explained that this was because according to the standard "An assignment expression has the value of the left operand after the assignment, [...]".
Strangely, removing the cast-to-void changes the behaviour: the load disappears. The user's happy, but I'm just confused.
So I also checked this out in a couple of versions of GCC (3.3 and 4.4). There, the compiler never generates a load, even if the value is explicitly used, e.g.
int TestTwo(void)
{
return (MY_REGISTER = 1);
}
Turns into
TestTwo:
move regA, 1
store regA, MY_REGISTER
move returnValue, 1
return
Does anyone have a view on which is a correct interpretation of the standard? Should the read-back happen at all? Is it correct or useful to add the read only if the value is used or cast to void?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
标准中的相关段落是这样的
所以这显然使得“左操作数的值”和存储值的更新之间存在差异。另请注意,返回值不是左值(因此表达式的返回值中没有对变量的引用)并且所有限定符都会丢失。
因此,我将其理解为当 gcc 返回它有意存储的值时,它做了正确的事情。
编辑:
即将推出的标准计划通过添加脚注来澄清这一点:
编辑2:
实际上,还有另一段关于表达式语句的内容可能会阐明这一点:
由于这意味着这样的语句不需要返回值的效果,因此强烈表明该值只能从如果使用该值则为变量。
总而言之,当您的客户看到变量已加载时,他确实会感到不安。如果你进一步解释的话,这种行为可能符合标准,但它显然处于可接受的边缘。
The relevant paragraph in the standard is this
So this clearly makes the difference between "the value of the left operand" and the update of the stored value. Also note that the return is not an lvalue (so there is no reference to the variable in the return of the expression) and all qualifiers are lost.
So I read this as gcc doing the right thing when it returns the value that it knowingly has to store.
Edit:
The upcoming standard plans to clarify that by adding a footnote:
Edit 2:
Actually there is another paragraph about expression statements that might shed a light on that:
Since this implies that the effect of returning a value is not wanted for such a statement, this strongly suggests that the value may only be loaded from the variable if the value is used.
As a summary, your customer really is rightly upset when he sees that the variable is loaded. This behavior might be in accordance with the standard if you stretch the interpretation of it, but it clearly is on the borderline of being acceptable.
读回似乎更接近标准(特别是考虑到读取 易失性变量可能会导致与写入的值不同的值),但我很确定这不是大多数使用易失性的代码所期望的,特别是在读取或写入易失性变量会触发一些其他效果的上下文。
一般而言, 挥发性 没有很好的定义——“什么构成了对一个对象的访问
具有易失性限定类型是实现定义的。”
编辑:如果我必须制作一个编译器,我想我不会读回该变量(如果未使用)并重新读取它(如果使用),但会发出警告。然后应该强制转换为 void
没有任何理由
不这样做,但无论如何,我会发出警告,解释如何获得其他效果。
肯定应该是一个,考虑到这一点,我 如果您从事编译器工作,您可能有人与 C 委员会联系,填写正式的缺陷报告将为您带来具有约束力的解释(好吧,存在 DR 被分类为“非缺陷”的风险,而没有任何关于什么的提示他们想要……)
Reading back seems to be nearer to the standard (especially considering that reading a volatile variable can result in a different value than the one written), but I'm pretty sure it isn't what is expected by most code using volatile, especially in contexts where reading or writing a volatile variable triggers some other effects.
volatile in general isn't very well defined -- "What constitutes an access to an object that
has volatile-qualified type is implementation-defined."
Edit: If I had to make a compiler, I think I wouldn't read back the variable if it isn't used and reread it if is, but with a warning. Then should a cast to void be an used?
should surely be one, and considering that, I don't any reason for
not to be. But in any case, I'd give a warning explaining how to get the other effect.
BTW, If you work on a compiler, you probably have someone in contact with the C committee, filling a formal defect report will bring you a binding interpretation (well, there is the risk of the DR being classified "Not A Defect" without any hint about what they want...)
标准中的语言没有提到读取 volatile 变量,只提到赋值表达式的值是什么,其中 a) 是由 C 语义定义的,而不是由变量的内容定义的,b) 在这里不使用,所以需要不被计算。
The language in the standard says nothing about reading the volatile variable, only what the value of the assignment expression is, which a) is defined by C semantics, not by the content of the variable and b) isn't used here, so need not be calculated.