我在使用某些代码时遇到一些问题(见下文)。当所有优化都关闭(-O0)时,代码的行为符合我的预期,但是当我打开优化时,代码的表现并不符合我的预期。因此,我在这里发帖,看看是否有人看到优化开启时代码运行方式不同(不正确)的原因。让我解释一下代码的作用...
TPM0->CNT 是一个定时器寄存器,属于 volatile uint32_t 类型。当 TPM0->CNT 达到最大值并翻转时,会生成中断。在此中断内,sg_CpuCount 按自上次中断以来经过的定时器周期数递增。
GetCpuCount 函数的工作是返回瞬时计数(即 sg_CpuCount + TPM0->CNT),但是由于中断可能随时发生并且计时器不会停止,因此在读取/计算此值时必须小心。读取两个组件,然后再次测试第一个组件以查看它是否发生变化。如果它改变了,则在第一次和第二次读取之间触发一个中断,并且重复该过程,但是如果它们相同,则知道没有发生中断,并且返回组件的添加。
逻辑合理吗?优化是否会产生糟糕的代码?有什么见解吗?提前致谢!
编辑:我应该提到优化开启时我注意到的行为。基本上,对 GetCpuCount 的后续调用并不总是像优化关闭时那样单调增加。也就是说,稍后调用 GetCpuCount 可能会返回比之前调用更小的数字 - 这是不可能的。
edit2:我应该提到运行此代码的微型计算机是 32 位架构(并且我使用的是 64 位值)。我仍然认为应该没问题,但这是一个需要考虑的问题。
static volatile int64_t sg_CpuCount;
void TPM0_IRQHandler(void)
{
TPM0->SC |= TPM_SC_TOF(1); //clear interrupt flag
sg_CpuCount += CPU_CLOCK / TICKRATE_HZ;
}
int64_t GetCpuCount(void)
{
for (;;) {
int64_t tmpCpuCount = sg_CpuCount;
uint32_t tmpCnt = TPM0->CNT;
if (tmpCpuCount == sg_CpuCount) {
return tmpCpuCount + tmpCnt;
}
}
}
edit3:添加asm
优化(-0s)
GetCpuCount:
.LFB36:
.loc 2 80 0
.cfi_startproc
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
push {r4, r5, r6, lr}
.cfi_def_cfa_offset 16
.cfi_offset 4, -16
.cfi_offset 5, -12
.cfi_offset 6, -8
.cfi_offset 14, -4
.LBB2:
.loc 2 82 0
ldr r4, .L20
.loc 2 83 0
ldr r6, .L20+4
.L18:
//int64_t tmpCpuCount = sg_CpuCount;
ldr r0, [r4]
ldr r1, [r4, #4]
//uint32_t tmpCnt = TPM0->CNT;
ldr r5, [r6, #4]
//if (tmpCpuCount == sg_CpuCount) {
ldr r2, [r4]
ldr r3, [r4, #4]
cmp r0, r2
bne .L18
cmp r1, r3
bne .L18
//return tmpCpuCount + tmpCnt;
movs r0, r5
movs r1, #0
.LBE2:
.loc 2 88 0
@ sp needed
.LBB3:
.loc 2 85 0
adds r0, r0, r2
adcs r1, r1, r3
.LBE3:
.loc 2 88 0
pop {r4, r5, r6, pc}
.L21:
.align 2
.L20:
.word sg_CpuCount
.word 1073971200
.cfi_endproc
未优化 (-00)
GetCpuCount:
.LFB36:
.loc 2 80 0
.cfi_startproc
@ args = 0, pretend = 0, frame = 16
@ frame_needed = 1, uses_anonymous_args = 0
push {r4, r7, lr}
.cfi_def_cfa_offset 12
.cfi_offset 4, -12
.cfi_offset 7, -8
.cfi_offset 14, -4
sub sp, sp, #20
.cfi_def_cfa_offset 32
add r7, sp, #0
.cfi_def_cfa_register 7
.L19:
.LBB2:
//int64_t tmpCpuCount = sg_CpuCount;
ldr r3, .L21
ldr r4, [r3, #4]
ldr r3, [r3]
str r3, [r7, #8]
str r4, [r7, #12]
//uint32_t tmpCnt = TPM0->CNT;
ldr r3, .L21+4
ldr r3, [r3, #4]
str r3, [r7, #4]
//if (tmpCpuCount == sg_CpuCount) {
ldr r3, .L21
ldr r4, [r3, #4]
ldr r3, [r3]
ldr r0, [r7, #8]
cmp r0, r3
bne .L19
ldr r0, [r7, #12]
cmp r0, r4
bne .L19
//return tmpCpuCount + tmpCnt;
ldr r3, [r7, #4]
movs r1, r3
movs r3, #0
movs r2, r3
ldr r3, [r7, #8]
ldr r4, [r7, #12]
adds r3, r3, r1
adcs r4, r4, r2
.LBE2:
.loc 2 88 0
movs r0, r3
movs r1, r4
mov sp, r7
add sp, sp, #20
@ sp needed
pop {r4, r7, pc}
.L22:
.align 2
.L21:
.word sg_CpuCount
.word 1073971200
.cfi_endproc
最佳答案
如果您使用的是 32 位计算机,则无法保证 64 位操作是原子的。由于优化,时间可能会发生变化。并且永远不要在禁用中断的情况下调用该函数。
static volatile uint32_t sg_CpuCount;
void TPM0_IRQHandler(void)
{
TPM0->SC |= TPM_SC_TOF(1); //clear interrupt flag
sg_CpuCount++; //count interrupts
}
int64_t GetCpuCount(void)
{
for (;;) {
uint32_t tmpCpuCount = sg_CpuCount;
uint32_t tmpCnt = TPM0->CNT;
if (tmpCpuCount == sg_CpuCount) {
return (int64_t)tmpCpuCount * CPU_CLOCK / TICKRATE_HZ + tmpCnt;
}
}
}
关于c - C/C++ 优化如何影响 volatile 变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43896407/