阅读 this question 的答案时,我注意到 register 在 C++17 中不再是有效的存储说明符。一些评论甚至表明编译器已经忽略 register 一段时间了。

我将 GCC 6.x 与 ARM Cortex-M MCU 一起使用,并且有一些带有内联汇编的代码,这些代码绝对需要在寄存器中有一个变量。以前我认为 register 关键字会为我做这件事,但显然它没有。

  • 在现代 C++ 中,确保编译器始终为给定变量使用寄存器的正确方法是什么?
  • 如果没有标准方法,是否有特定于 GCC 的方法来执行此操作?也许某种属性?还是编译器特定的关键字?

  • 编辑 :为什么我需要在寄存器中存储一些东西?
    我正在使用 ARM LDREX/STREX 指令实现无锁环形缓冲区。我需要将 ARM LDREX 指令的结果存储在寄存器中,因为将其存储在内存中会破坏 Cortex-M 上的整个机制。

    编辑 :示例代码。

    这是从环形缓冲区中截取的代码片段,用于说明问题的要点。兴趣点是 __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++ 或 C 代码中)。您需要 信任您的编译器 ,因此您甚至不应该这样做。

    请注意:

  • 最近的 C 和 C++ 标准(例如 C11 或 C++14 或 C++17)并没有以命令的方式谈论处理器寄存器,他们提到 register 关键字 只是 (在上个世纪) 编译器的提示
  • 一些处理器(至少在过去)甚至没有任何真正的程序员可访问的处理器寄存器。
  • 最重要的是,您应该 相信您的编译器足够好 optimizations 并且在某些情况下,将值放入寄存器对于性能来说并不是最好的(特别是因为该寄存器可以更好地用于其他一些值)。

  • 但是,作为 扩展 GCC 编译器使您能够放置 variable in a specified register 我不建议在没有充分理由的情况下使用 (至少确保在使用和不使用该功能的情况下对代码进行基准测试)。

    您确实需要了解 当前编译器在大多数情况下都是 优化得比 更好。在尝试手动优化之前,请务必对您的代码进行基准测试(例如,使用 g++ -O3 和适当的 -mtune= 参数编译)。对于性能敏感的例程,还要检查生成的汇编代码(例如使用 g++ -O3 -fverbose-asm -S )。



    然后我建议使用一个小的 extended assembler 代码(用于 GCC),或者,如果绝对必要,声明一个 variable in a specified register

    也许您还需要使用 -ffixed- reg 选项编译所有代码(包括任何使用过的库,包括标准 C 和 C++ 库!)。

    但我坚持:你需要比现在更信任你的编译器。您确定找不到(并且可能从源代码配置和构建)最近的 GCC(例如 GCC 7),它可以作为内置或其他东西启用您的低级同步机制?

    关于c++ - 告诉编译器我希望变量始终存储在寄存器中的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44721905/

    10-11 22:51
    查看更多