12

I know that in modern compilers with optimizations, declaring a variable as register is merely a suggestion, and the compiler may ignore it, placing the variable in RAM instead of an actual register.

However, I’m curious about how older compilers (say, from the '80s or early '90s) handled this. If I declared more variables as register than the number of available registers on the CPU, what would those older compilers do?

Would they:

  1. Place the extra variables in RAM?
  2. Generate some kind of warning or error?
  3. Handle this scenario differently, depending on the specific compiler?

Any insights or examples from older systems would be greatly appreciated!

6
  • 32
    register has always been "merely a suggestion" from the moment it was introduced. Commented Sep 19, 2024 at 15:05
  • 6
    Seconding @DaveTweed - my 1991 Harbison and Steele third edition states "register - May be used for local variables or parameter declarations ... it provides a hint to the compiler that the object will be heavily used and should be allocated in a way that minimizes access time." Which only leads to an English language question - is a hint a suggestion or not... Commented Sep 19, 2024 at 15:37
  • @DaveTweed : It depends. In embedded one can ever specify which exact register it will be stored in. But that depends on the specific compiler tuned to that specific microcontroller. Commented Sep 20, 2024 at 6:20
  • 3
    The only thing register does is forbidding taking the address of the variable. Commented Sep 20, 2024 at 9:12
  • 1
    @PatrickSchlüter: When using gcc-ARM with -O0, the first few objects in a function declared with register storage class will be assigned registers, which may allow code generation that's as good (or on rare occasions) better than what the compiler would produce at higher optimization settings. Commented Sep 20, 2024 at 21:14

3 Answers 3

23

A typical pattern was for a compiler to assign a register to any register-class object if one was available, and otherwise treat the definition like any other. Turbo C would do this for the first two exactly-16-bit variable declarations, placing them in SI and DI, and GCC-ARM even today, when using optimization level zero, will behave likewise with some of the numbered ARM registers. People often said, then as now, that compilers are better than people at figuring out what belongs in registers, and sometimes that's true but other times it isn't. Some compilers will employ registers that aren't committed to holding register-qualified objects to hold a "recently used values" cache, and having more registers available can increase the effectiveness of this. Using register storage class for an object that goes unused for part of a function may degrade efficiency in that part more than it improves the efficiency of the places where the object would be used. On the other hand, many older compilers' register-caching logic is designed merely to reuse values that already happen to be in registers, rather than to defer and consolidate writes. Thus, if code performs x+=a; and then later x+=b; and x+=c;, a compiler processing x+=b; may recognize that it has the value written by x+=a; in a register, so it can add b to that without having to reload it first, and then store the result, and then later recognize that it has the value written by x+=b; in a register, so it can add c to that and store it. This would improve efficiency versus having to load x three times, but fail to eliminate the stores.

14
  • Sorry for the disturbance,your answer wasn't clear to me this is what you explained "Older compilers would assign registers to variables when possible, but if you declared more register variables than available registers, the extra ones would be stored in RAM" my understanding is correct or not, and if not please feel free to correct me Commented Sep 19, 2024 at 15:20
  • 1
    @NalanPandiKumar: Yes. A typical compiler upon encountering a variable declaration would say "If this has register storage class, and it is of a type that matches an available register, assign it that register; otherwise, assign it a stack-relative address with a displacement equal to the total space reserved for such variables (possibly adding 1-3 bytes if needed for alignment), and adjust the total space reservation accordingly. Commented Sep 19, 2024 at 15:23
  • 3
    @NalanPandiKumar: In many cases, compilers dealt with alignment by padding any individual named object to the coarsest requied alignment. If one declared char a,b,c,d;, a compiler for the 68000 (which is designed around the assumption that the stack pointer should remain 32-bit aligned) would likely allocate 16 bytes, but given char a[4]; it would only reserve four. Commented Sep 19, 2024 at 15:24
  • 2
    'Allocate in RAM' is also merely a suggestion to an optimizing compiler. e.g. { int ans = 42; printf("answer: %d%n"); } has no need to store the 'variable' anywhere. Commented Sep 20, 2024 at 20:26
  • @dave Sorry I can't understand.It is possible to not store the variable anywhere, because I don't know ,then how does the variable is bind with a memory address. Commented Sep 21, 2024 at 2:02
6

I know that in modern compilers with optimizations, declaring a variable as register is merely a suggestion, and the compiler may ignore it, placing the variable in RAM instead of an actual register.

You don't need to do that at all on any semi-modern compiler (from the late 1990s somewhere).

Very old compilers on the other hand (1980s) were plain dumb and would likely just allocate every declared variable on the stack unless you explicitly told it otherwise with the register keyword, which is pretty much obsolete today. It would perhaps only use registers for temporary storage in the middle of a calculation.

But if we assume that we are dealing with a compiler which at least will make some half-hearted attempt of optimizing the code... Then it is important to realize that the place where a variable is declared in C is not necessarily the point in time when that variable gets allocated.

Most compilers would at least be smart enough to know which registers that are occupied internally. And at the point when a value in a register is no longer of any use, the compiler would internally flag that one as available. It will only use stack allocation when it runs out of registers.

And so if the compiler only register allocates variables just before using them, it reduces the need to involve the stack. Even if you declared 100 variables you aren't using all of them simultaneously.

So the amount of registers used at a given time will be restricted to the amount of data the compiler needs to juggle simultaneously at that point. This is why you can write quite efficient C even for small dysfunctional cores like 8 bit MCUs where the amount of registers are severely limited - they are basically 1980s technology.

One exception to all of the above is the ABI calling convention. When data is passed to/from functions, then the compiler must fall in line pre-allocate certain types of variables according to the ABI. Unless the compiler is able to inline the function entirely, which wasn't likely at all even in the 1990s. The reason why the inline keyword was added to C99 was because compilers did such a poor job at automatic inlining. Today this keyword too is mostly obsolete.

1
  • Thanks for the information ,The compiler only needs to allocate register when it needs to process that instructions,Eventhough 100 of them are declared is a valid point. In very old compilers the scope of the variable where it declared will be used as the storage area when the cpu runs out of register. Yeah I understood thanks for the information. Commented Oct 11, 2024 at 3:05
5

The register keyword had (on SunOS4 compilers on SPARC around 1990), for a variable declared as register int x; two effects:

  • it forbids using the unary adress-of operation (e.g &x).
  • it allocated a machine register to keep (if there are enough of them) the value in it.

If I remember correctly, when used too often, the lexically last register annotations have been ignored (for allocating a machine register), and the variable was on the stack.

I am not sure that these old 1989 compilers claimed compliance with C89.

Remember also that the SPARC architecture had register windows and more of them on the chip itself.

1
  • 1
    Forbidding the & operator for register qualified variables is a requirement from ISO C89 and not unique to any particular system. As for how compilers behave pre C89 (drafts), I don't know. Commented Oct 10, 2024 at 13:24

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.