C++ 告诉编译器我希望变量始终存储在寄存器中的正确方法是什么?
阅读了它的答案后,我注意到,C++ 告诉编译器我希望变量始终存储在寄存器中的正确方法是什么?,c++,gcc,arm,inline-assembly,cortex-m,C++,Gcc,Arm,Inline Assembly,Cortex M,阅读了它的答案后,我注意到,register在C++17中不再是有效的存储说明符。一些评论甚至暗示编译器已经忽略了寄存器一段时间了 我将GCC 6.x与ARM Cortex-M MCU结合使用,并使用了一些内联汇编代码,这些代码绝对需要在寄存器中包含一个变量。以前我假设register关键字可以为我实现这一点,但显然不行 在现代C++中,确保编译器总是为给定变量使用登记器的正确方法是什么? 如果没有标准的方法,是否有GCC特定的方法?也许是某种属性?还是编译器特定的关键字 编辑:为什么我需要
register
在C++17中不再是有效的存储说明符。一些评论甚至暗示编译器已经忽略了寄存器
一段时间了
我将GCC 6.x与ARM Cortex-M MCU结合使用,并使用了一些内联汇编代码,这些代码绝对需要在寄存器中包含一个变量。以前我假设register
关键字可以为我实现这一点,但显然不行
-
在现代C++中,确保编译器总是为给定变量使用登记器的正确方法是什么?
- 如果没有标准的方法,是否有GCC特定的方法?也许是某种属性?还是编译器特定的关键字
我正在使用ARM
LDREX
/STREX
指令实现一个无锁环形缓冲区。我需要将ARMLDREX
指令的结果存储在寄存器中,因为将其存储在内存中会破坏Cortex-M上的整个机制
编辑:示例代码
这是从环形缓冲区中截取的代码片段,用于说明问题的要点。关注点包括\uuuu LDREXW
、\uuuu STREXW
和\uuuu CLREX
,这些都在cmsis\u gcc.h
中定义。我使用它们来实现无锁机制
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 ...
}
模板
类环形缓冲区最终{
__属性(对齐(8)))
T缓冲区[maxCount];
uint32\u t启动;
uint32_t end;
布尔后推(常数T和项目){
注册uint32_t独占;
寄存器uint32\u t oldEnd;
做{
//仅加载当前结束值
排他性终止=uu LDREXW(&end);
__DMB();
//记住旧的结束值,以便
//我们可以把物品存放在那个地方
oldEnd=排他性;
//检查环形缓冲区是否已满
如果(isFull()){
__CLREX();
__DMB();
返回false;
}
//找出正确的新值
if(exclusiveEnd==(最大计数-1)){
排他性d=0;
}
否则{
排他性d++;
}
//尝试存储新的结束值
}而(0!=uu STREXW(排他性,&end));
__CLREX();
__DMB();
//储存新物品
//memcpy(缓冲区+旧端和项目,大小(T));
缓冲区[oldEnd]=项目;
返回true;
}
//…其他方法。。。
}
为什么必须将LDREX
结果存储在寄存器中:
在Cortex-M4上,实现的独占保留颗粒是整个内存地址范围(引用自Cortex-M4 TRM),这意味着如果存储LDREX
结果的变量最终进入内存而不是寄存器,则以下STREX
将始终失败
注:此代码运行在“裸露”硬件上,没有操作系统,例如
<代码>登记> /COM>,在C++ 1998版之前,甚至被视为第一个标准。而且,在很多情况下,编译器在寄存器分配方面比程序员做得好得多,所以它忽略了这个提示
<>标准C++中没有通用的或可移植的方式(即与来自不同厂商的编译器和不同主机系统一起工作),以确保将特定变量放置在登记器中。 对于某些编译器,可以使用内联汇编程序显式使用寄存器。这种方法的缺点是内联汇编程序因编译器和主机而异(本质上,它是由实现定义的)。一些现代编译器也具有足够的攻击性,它们还优化了内联汇编程序,因此即使在内联汇编程序中也可以删除寄存器的使用。分析和转换通常相对简单(至少与其他类型的优化相比),因此即使在优化设置较低的情况下也可能发生 确定的唯一方法是检查输出汇编程序,以确定编译器对您选择的设置(优化等)执行的操作可能是唯一可靠的方法是在汇编程序中编写代码,而不是C++中的内嵌汇编程序。(我不知道有哪一个汇编程序能在很大程度上优化代码,但我也从来没有理由尝试找到一个)。根据定义,这在系统之间是不可移植的——汇编程序通常依赖于机器
你的用词 以前我假设register
关键字可以为我实现这一点,但显然不行
这也引发了另一个潜在的担忧——您正试图不必要地强制使用寄存器
如果您之前假设register
关键字足够了,并且您的程序似乎已按要求运行,那么您很可能不必担心寄存器中是否存在任何变量
我的观点是,如果性能非常关键,足以证明将任何变量强制放入寄存器是正确的,那么您应该知道编译器正在将您选择的变量放入寄存器,而不仅仅是假设是这样。如果您刚刚假设这是真的,并且没有遇到困难,那么很可能编译器忽略了提示,并实现了代码所需的性能
我建议您需要质疑并重新验证您的信念,即您的特定变量实际上需要存储在寄存器中
没有关于您的设计需求的信息,也没有通过大量测试证明编译后的代码在实际场景中不满足这些需求