这是编译器的错误吗?

4

我使用基于8051微控制器的嵌入式系统,该系统有一个调试串口。我编写了一个简单的串口输出函数,一直运行良好,直到我对其进行了一些小修改以减少内存占用。然后以下代码行不再起作用(并且不是修改的一部分)...

dbg_TxBuf[(dbg_TxBufProduceCount++) & (sizeof(dbg_TxBuf) - 1)] = ch;

...无法正常工作。变量dbg_TxBufdbg_TxBufProduceCount是全局变量,仅由输出函数和串口ISR使用(这些都没有改变):

#define DBG_TX_BUFFER_SIZE 16 // MUST be a power-of-2 (this line is actually in a separate file, not that it matters)
volatile uint8_t xdata dbg_TxBuf[DBG_TX_BUFFER_SIZE]; // must be sized by a power-of-2, and MUST BE LESS THAN 256 since the 'count' vars are uint8
volatile uint8_t xdata dbg_TxBufProduceCount; // akin to a 'head' index but more useful since it allows use of every byte in the buf
volatile uint8_t xdata dbg_TxBufConsumeCount; // akin to a 'tail' index but more useful since it allows use of every byte in the buf

具体来说,编译器现在优化代码行的方式是,在将ch写入dbg_TxBuf之前,先在内存中递增dbg_TxBufProduceCount。然后串口ISR“偶尔”(实际上相当频繁)发现dbg_TxBufConsumeCount!=dbg_TxBufProduceCount,并在输出函数将ch写入之前读取dbg_TxBuf [(dbg_TxBufConsumeCount++)& (sizeof(dbg_TxBuf)-1)]。因此,我在串口上获得了损坏的输出。

这是该行的8051反汇编:

935>       dbg_TxBuf[(dbg_TxBufProduceCount++) & (sizeof(dbg_TxBuf) - 1)] = ch;
DC84: 9006D6   MOV   DPTR,#dbg_TxBufProduceCount
DC87: E0       MOVX  A,@DPTR    <--- loads the value in dbg_TxBufProduceCount into the A register
DC88: FE       MOV   R6,A       <--- saves a copy of it in R6
DC89: 04       INC   A          <--- increments it
DC8A: F0       MOVX  @DPTR,A    <--- writes the incremented value 
DC8B: EE       MOV   A,R6       <--- gets the original copy of ProduceCount back in A
DC8C: 7C00     MOV   R4,#00     Begin computing address of dbg_TxBuf[~]...
DC8E: 540F     ANL   A,#0F      <--- A = A & (sizeof(dbg_TxBuf) - 1)
DC90: 24D8     ADD   A,#0D8     <--- A is now the low byte of &dbg_TxBuf[~]
DC92: F582     MOV   DPL,A      <--- put that in DPL
DC94: EC       MOV   A,R4       <--- (an inefficient way of loading the...
DC95: 3406     ADDC  A,#06      <---   ...immediate value 0x06 into A)
DC97: F583     MOV   DPH,A      <--- DPTR now points to dbg_TxBuf[~]
DC99: EF       MOV   A,R7       <--- load 'ch' into A
DC9A: F0       MOVX  @DPTR,A    <--- write 'ch' to *DPTR

从“本地”角度来看,编译器正确地处理了后缀递增运算符,即在计算dbg_TxBuf中的索引时使用了dbg_TxBufProduceCount的值,而在递增之前。但是从“全局”角度来看,我认为它没有正确处理事件序列,特别是因为我将dbg_TxBuf []dbg_TxBufProduceCount都声明为volatile。编译器不应该在写入ch到内存之后才将递增后的dbg_TxBufProduceCount值写入内存吗?
顺便说一下,我正在使用Keil 8051 C编译器v7.10。我不知道v7.10是什么时候发布的,但似乎我们对编译器的支持在2005年5月就结束了。

[mcve]在哪里?编译器可能严格实现抽象机器。您可能想阅读“volatile”限定符对您的作用。 - too honest for this site
2
太多的手势了。查看C代码比汇编更有帮助。 - Eugene Sh.
1
@Olaf - 我肯定已经掌握了“最小化”的部分。如果我包括所有内容,包括ISR,那么提供“完整”部分会有点冗长,因此我包含了汇编代码,以防有人需要更多细节。实际上,问题的关键完全包含在我的“具体而言正在发生什么......”段落和接近结尾的“从‘本地’角度来看......”段落中。其余部分只是不太重要的细节。 - phonetagger
@phonetagger:我能告诉你的是,这不是编译器的错误。有时候最好不要把所有东西都放在一个语句中。 - too honest for this site
@EugeneSh. - 由于我担心这是编译器的错误(根据David Schwartz的回答,显然不是),所以在决定是否存在编译器错误时,汇编代码似乎是非常关键的信息。我认为我的问题中的信息非常仔细和合理地选择,以回答问题,而不提供许多不必要的废话。我认为我在这里没有做任何手势。 - phonetagger
1个回答

6

编译器不应该在将ch写入内存后,将dbg_TxBufProduceCount的自增值写入内存吗?

不应该。没有任何理由要求它这样做。在单个语句中进行的操作之间没有任何顺序限制。这是一条写入两个易失变量的单条语句——它可以按任何顺序写入这些变量。从概念上讲,这与以下代码没有任何区别:

i = a++ + b++;

即使ab是易失性的,编译器仍然可以自由地生成代码以任意顺序写入它们。单个语句的部分评估顺序以及该指令的副作用的可见性顺序是未指定的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接