【发布时间】:2017-06-23 12:50:09
【问题描述】:
阅读this question 的答案后,我注意到register 不再是C++17 中的有效存储说明符。一些 cmets 甚至暗示编译器已经忽略 register 有一段时间了。
我使用带有 ARM Cortex-M MCU 的 GCC 6.x,并且有一些带有内联汇编的代码,这些代码绝对需要在寄存器中有一个变量。以前我认为register 关键字会为我做这个,但显然它没有。
- 在现代 C++ 中,确保编译器始终为给定变量使用寄存器的正确方法是什么?
- 如果没有标准方法,是否有特定于 GCC 的方法来执行此操作?也许某种属性?还是编译器特定的关键字?
编辑:为什么我需要在寄存器中存储一些东西?
我正在使用 ARM LDREX / STREX 指令实现无锁环形缓冲区。我需要将 ARM LDREX 指令的结果存储在寄存器中,因为将其存储在内存中会破坏 Cortex-M 上的整个机制。
编辑:示例代码。
这是从环形缓冲区中截取的代码sn-p,用于说明问题的重点。兴趣点是__LDREXW、__STREXW 和__CLREX,它们都在cmsis_gcc.h 中定义。 They are intrinsic functions of the ARM synchronization primitives.我用它们来实现无锁机制。
template<typename T, uint32_t maxCount>
class RingBuffer final {
__attribute__((aligned(8)))
T buffer[maxCount];
uint32_t start;
uint32_t end;
bool pushBack(const T &item) {
register uint32_t exclusiveEnd;
register uint32_t oldEnd;
do {
// Load current end value exclusively
exclusiveEnd = __LDREXW(&end);
__DMB();
// Remember old end value so that
// we can store the item at that location
oldEnd = exclusiveEnd;
// Check if ring buffer is full
if (isFull()) {
__CLREX();
__DMB();
return false;
}
// Figure out correct new value
if (exclusiveEnd == (maxCount - 1)) {
exclusiveEnd = 0;
}
else {
exclusiveEnd ++;
}
// Attempt to store new end value
} while (0 != __STREXW(exclusiveEnd, &end));
__CLREX();
__DMB();
// Store new item
//memcpy(buffer + oldEnd, &item, sizeof(T));
buffer[oldEnd] = item;
return true;
}
// ... other methods ...
}
为什么LDREX 结果必须存储在寄存器中:
在 Cortex-M4 实现的独占保留颗粒是整个内存地址范围(引用自 Cortex-M4 TRM),这意味着如果存储 LDREX 结果的变量最终在内存中而不是寄存器,那么下面的STREX 总是会失败。
注意:此代码在“裸机”硬件上运行,没有操作系统等。
【问题讨论】:
-
在 C++ 中没有办法做到这一点,除非在内联汇编中编写所有内容。
-
@nwp - 内联汇编可能也不会这样做;编译器可以重新排列您的代码。真正知道的唯一方法是编写一个单独的 ASM 文件。
-
为什么它绝对需要在寄存器中?表现?还是有其他原因?如果只是性能,那么你可以尝试用纯 C++ 编写,看看优化器是否正确。
-
在单独的
asm语句中使用ldrex/strex一直是对内联汇编的无效使用。 -
@stark 无法保证您的方法不会将寄存器溢出到堆栈中。
标签: c++ gcc arm inline-assembly cortex-m