在 C 中,如果 B 是易失性的,则表达式 (void)(B = 1) 应该读取 B

发布于 2024-10-19 10:10:46 字数 785 浏览 1 评论 0原文

我负责几个嵌入式平台的编译器。一位用户最近抱怨我们的一个编译器出现以下行为。给定如下代码:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

何以畏孤独 2024-10-26 10:10:46

标准中的相关段落是这样的

赋值运算符存储一个值
在左边指定的对象中
操作数。赋值表达式有
之后左操作数的值
赋值,但不是左值。
赋值表达式的类型
是左操作数的类型,除非
左操作数具有限定类型,
在这种情况下,它是不合格的
左边的版本类型
操作数。更新左操作数的存储值的副作用应
发生在上一个和下一个序列点之间。

所以这显然使得“左操作数的值”和存储值的更新之间存在差异。另请注意,返回值不是左值(因此表达式的返回值中没有对变量的引用)并且所有限定符都会丢失。

因此,我将其理解为当 gcc 返回它有意存储的值时,它做了正确的事情。

编辑:

即将推出的标准计划通过添加脚注来澄清这一点:

允许实施
读取对象以确定值
但并不要求这样做,即使
对象具有易失性限定类型。

编辑2:

实际上,还有另一段关于表达式语句的内容可能会阐明这一点:

表达式中的表达式
语句被评估为无效
其副作用的表达式。\footnote{例如具有副作用的赋值和函数调用}

由于这意味着这样的语句不需要返回值的效果,因此强烈表明该值只能从如果使用该值则为变量。

总而言之,当您的客户看到变量已加载时,他确实会感到不安。如果你进一步解释的话,这种行为可能符合标准,但它显然处于可接受的边缘。

The relevant paragraph in the standard is this

An assignment operator stores a value
in the object designated by the left
operand. An assignment expression has
the value of the left operand after
the assignment, but is not an lvalue.
The type of an assignment expression
is the type of the left operand unless
the left operand has qualified type,
in which case it is the unqualified
version of the type of the left
operand. The side effect of updating the stored value of the left operand shall
occur between the previous and the next sequence point.

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:

The implementation is permitted to
read the object to determine the value
but is not required to, even when the
object has volatile-qualified type.

Edit 2:

Actually there is another paragraph about expression statements that might shed a light on that:

The expression in an expression
statement is evaluated as a void
expression for its side effects.\footnote{Such as assignments, and function calls which have side effects}

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.

长伴 2024-10-26 10:10:46

读回似乎更接近标准(特别是考虑到读取 易失性变量可能会导致与写入的值不同的值),但我很确定这不是大多数使用易失性的代码所期望的,特别是在读取或写入易失性变量会触发一些其他效果的上下文。

一般而言, 挥发性 没有很好的定义——“什么构成了对一个对象的访问
具有易失性限定类型是实现定义的。”

编辑:如果我必须制作一个编译器,我想我不会读回该变量(如果未使用)并重新读取它(如果使用),但会发出警告。然后应该强制转换为 void

(void) v;

没有任何理由

(void) v = exp;

不这样做,但无论如何,我会发出警告,解释如何获得其他效果。

肯定应该是一个,考虑到这一点,我 如果您从事编译器工作,您可能有人与 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?

(void) v;

should surely be one, and considering that, I don't any reason for

(void) v = exp;

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...)

メ斷腸人バ 2024-10-26 10:10:46

标准中的语言没有提到读取 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文