Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to tell the compiler that I want a variable to be always stored in a register?

Reading the answers of this question it came to my attention that register is no longer a valid storage specifier in C++17. Some comments even suggest that the compiler had already ignored register for some time.

I use GCC 6.x with an ARM Cortex-M MCU and have some piece of code with inline assembly which absolutely need to have a variable in a register. Previously I had assumed that the register keyword will do this for me, but apparently it doesn't.

  • In modern C++, what is the correct way to ensure that the compiler always uses a register for a given variable?
  • If there isn't a standard way, is there a GCC-specific way of doing this? Maybe some sort of attribute? Or a compiler specific keyword?

EDIT: Why do I need to store something in a register?
I'm implemeting a lock-free ring buffer with the ARM LDREX / STREX instructions. I need to store the result of the ARM LDREX instruction in a register, because storing it in memory would defeat the whole mechanism on a Cortex-M.

EDIT: Example code.

This is a code snippet cut from the ring buffer to illustrate the point of the question. The points of interest are __LDREXW, __STREXW and __CLREX which are all defined in cmsis_gcc.h. They are intrinsic functions of the ARM synchronization primitives. I use them to implement a lock-free mechanism.

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 ...

}

Why the LDREX result must be stored in a register:

On the Cortex-M4 the implemented exclusives reservation granule is the entire memory address range (quoted from Cortex-M4 TRM), which means if the variable storing the LDREX result ends up in memory instead of a register, then the following STREX will always fail.

NOTE: this code runs on "bare-metal" hardware, there is no operating system, etc.

like image 619
Venemo Avatar asked Jun 23 '17 12:06

Venemo


People also ask

How do compilers store variables?

Each variable is given a "stack offset", which is where in the stack it's stored. Then, when the program needs to access a variable x , the compiler replaces x with STACK_POINTER + x_offset , to get the actual physical place it's stored in memory.

How are variables stored in registers?

Register variables are stored in registers. Static variable is stored in the memory of the data segment. In register variables, CPU itself stores the data and access quickly.

Where does compiler store variables?

At some point, for local variables, the compiler assigns either a stack location or a CPU register (it can be more complex in that the variable can actually have multiple locations, such as a stack location for some parts of the generated code and a CPU register for other sections).

Why do we need to declare a variable as a register and what happens if we declare variable as a register?

Register variables tell the compiler to store the variable in CPU register instead of memory. Frequently used variables are kept in registers and they have faster accessibility. We can never get the addresses of these variables. “register” keyword is used to declare the register variables.


2 Answers

What is the correct way to tell the compiler that I want a variable to be always stored in a register?

You can't do that (in portable standard C++ or C code). You need to trust your compiler, so you should not even want to do that.

Notice that:

  • Recent C & C++ standards (e.g. C11 or C++14 or C++17) don't speak of processor registers in an imperative way, and they mention that the register keyword was (in the previous century) only a hint for compilers.

  • Some processors don't (at least in the past) even have any real programmer accessible processor register.

  • Most importantly, you should trust your compiler for good enough optimizations and in some cases putting a value in a register is not the best for performance (in particular, because that register could be better used for some other value).

However, as an extension the GCC compiler enables you to put a variable in a specified register. I don't recommend using that without very good reasons (at least be sure to benchmark your code with and without using that feature).

You really need to understand that current compilers are most of the time optimizing better than you can do. Be sure to benchmark your code (e.g. compiled with g++ -O3 and appropriate -mtune= argument) before trying to optimize by hand. For performance sensitive routines, examine also the generated assembler code (e.g. with g++ -O3 -fverbose-asm -S).

On the Cortex-M4 the implemented exclusives reservation granule is the entire memory address range (quoted from Cortex-M4 TRM),

Then I recommend either using a small extended assembler code (for GCC) or, if absolutely necessary, declare a variable in a specified register

Perhaps you also need to compile all your code (including any used library, including standard C and C++ libraries!) with -ffixed-reg option.

But I insist: you need to trust your compiler more than you currently do. Are you sure you can't find (and perhaps configure and build from source) a recent GCC (e.g. GCC 7) which enables, as a builtin or something else, your low-level synchronization mechanism?

like image 147
Basile Starynkevitch Avatar answered Oct 20 '22 14:10

Basile Starynkevitch


register has mostly been treated as a hint by C++ compilers, even before the first standard, which was ratified in 1998. And, in a lot of cases, the compiler has been able to do a significantly better job of register allocation than programmers, so it has ignored that hint.

There is no general or portable way in standard C++ (i.e. one that works with compilers from different vendors, and on different host systems) to ensure a particular variable is placed in a register.

It is possible, with some compilers, to use inline assembler to explicitly use registers. The catch with this approach is that inline assembler varies between compilers and hosts (essentially, it is implementation defined). Several modern compilers are also aggressive enough that they optimise inline assembler as well, so may remove usage of a register even in inline assembler. The analysis and transformations are often relatively simple (in comparison with other types of optimisations, at least), so may happen even with low optimisation settings.

The only way to know for sure is to examine the output assembler to determine what the compiler does, for your chosen settings (optimisation, etc).

Probably the only sure-fire way is to write your code in assembler, rather than as inline assembler in C++. (I'm not aware of any assembler that optimises code to a great extent, but I've never had reason to try and find one either). By definition, that is not portable between systems - assembler is often machine dependent.

Your choice of words

Previously I had assumed that the register keyword will do this for me, but apparently it doesn't.

also raises another potential concern - that you are attempting to force use of registers unnecessarily.

If you previously assumed the register keyword was sufficient, and your program appears to have worked as required anyway, it's quite possible that there is no need for you to worry if any variable is in a register.

My point is, if performance is critical enough to justify forcing any variable into a register, you should have known that your compiler was placing your chosen variable in a register, not just assumed that to be so. If you have just assumed that is true, and not encountered difficulties, it's quite possible the compiler was ignoring the hint, and achieving your required performance for your code anyway.

I suggest you need to question and revalidate your belief that your particular variable actually needs to be stored in a register.

Without information about your design requirements - and evidence through substantial testing that compiled code does not meet those requirements in realistic scenarios - the assumption that you need to use a register is often misplaced.

like image 2
Peter Avatar answered Oct 20 '22 13:10

Peter