Question

Hi I have been reading this kind of stuff in various docs

register

Tells the compiler to store the variable being declared in a CPU register.

In standard C dialects, keyword register uses the following syntax:

register data-definition;

The register type modifier tells the compiler to store the variable being declared in a CPU register (if possible), to optimize access. For example,

register int i;

Note that TIGCC will automatically store often used variables in CPU registers when the optimization is turned on, but the keyword register will force storing in registers even if the optimization is turned off. However, the request for storing data in registers may be denied, if the compiler concludes that there is not enough free registers for use at this place.

http://tigcc.ticalc.org/doc/keywords.html#register

My point is not only about register. My point is why would a compiler stores the variables in memory. The compiler business is to just compile and to generate an object file. At run time the actual memory allocation happens. why would compiler does this business. I mean without running the object file just by compiling the file itself does the memory allocation happens in case of C?

Was it helpful?

Solution

The compiler is generating machine code, and the machine code is used to run your program. The compiler decides what machine code it generates, therefore making decisions about what sort of allocation will happen at runtime. It's not executing them when you type gcc foo.c but later, when you run the executable, it's the code GCC generated that's running.

This means that the compiler wants to generate the fastest code possible and makes as many decisions as it can at compile time, this includes how to allocate things.

OTHER TIPS

The compiler doesn't run the code (unless it does a few rounds for profiling and better code execution), but it has to prepare it - this includes how to keep the variables your program defines, whether to use fast and efficient storage as registers, or using the slower (and more prone to side effects) memory.

Initially, your local variables would simply be assigned location on the stack frame (except of course for memory you explicitly use dynamic allocation for). If your function assigned an int, your compiler would likely tell the stack to grow by a few additional bytes and use that memory address for storing that variable and passing it as operand to any operation your code is doing on that variable.
However, since memory is slower (even when cached), and manipulating it causes more restrictions on the CPU, at a later stage the compiler may decide to try moving some variables into registers. This allocation is done through a complicated algorithm that tries to select the most reused and latency critical variables that can fit within the existing number of logical registers your architecture has (While confirming with various restrictions such as some instructions requiring the operand to be in this or that register).

There's another complication - some memory addresses may alias with external pointers in manners unknown at compilation time, in which case you can not move them into registers. Compilers are usually a very cautious bunch and most of them would avoid dangerous optimizations (otherwise they're need to put up some special checks to avoid nasty things).

After all that, the compiler is still polite enough to let you advise which variable it important and critical to you, in case he missed it, and by marking these with the register keyword you're basically asking him to make an attempt to optimize for this variable by using a register for it, given enough registers are available and that no aliasing is possible.

Here's a little example: Take the following code, doing the same thing twice but with slightly different circumstances:

#include "stdio.h"

int j;
int main() {
    int i;
    for (i = 0; i < 100; ++i) {
        printf ("i'm here to prevent the loop from being optimized\n");
    }
    for (j = 0; j < 100; ++j) {
        printf ("me too\n");
    }
}

Note that i is local, j is global (and therefore the compiler doesn't know if anyone else might access him during the run).

Compiling in gcc with -O3 produces the following code for main:

0000000000400540 <main>:
  400540:       53                      push   %rbx
  400541:       bf 88 06 40 00          mov    $0x400688,%edi
  400546:       bb 01 00 00 00          mov    $0x1,%ebx
  40054b:       e8 18 ff ff ff          callq  400468 <puts@plt>
  400550:       bf 88 06 40 00          mov    $0x400688,%edi
  400555:       83 c3 01                add    $0x1,%ebx            # <-- i++
  400558:       e8 0b ff ff ff          callq  400468 <puts@plt>
  40055d:       83 fb 64                cmp    $0x64,%ebx
  400560:       75 ee                   jne    400550 <main+0x10>
  400562:       c7 05 80 04 10 00 00    movl   $0x0,1049728(%rip)        # 5009ec <j>
  400569:       00 00 00
  40056c:       bf c0 06 40 00          mov    $0x4006c0,%edi
  400571:       e8 f2 fe ff ff          callq  400468 <puts@plt>
  400576:       8b 05 70 04 10 00       mov    1049712(%rip),%eax        # 5009ec <j> (loads j)
  40057c:       83 c0 01                add    $0x1,%eax            # <-- j++
  40057f:       83 f8 63                cmp    $0x63,%eax
  400582:       89 05 64 04 10 00       mov    %eax,1049700(%rip)        # 5009ec <j> (stores j back)
  400588:       7e e2                   jle    40056c <main+0x2c>
  40058a:       5b                      pop    %rbx
  40058b:       c3                      retq

As you can see, the first loop counter sits in ebx, and is incremented on each iteration and compared against the limit.
The second loop however was the dangerous one, and gcc decided to pass the index counter through memory (loading it into rax every iteration). This example serves to show how better off you'd be when using registers, as well as how sometimes you can't.

The compiler needs to translate the code into machine instruction, and tell the computer how to run the code. That include how to make operations (like multiply two numbers) and how to store the data (stack, heap or register).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top