1

In C, the register storage qualifier is an hint to the implementation that such identifier should be accessed as fast as possible (e.g. stored in a CPU register).

§6.7.1 A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined.

and

§6.7.3 The intended use of the restrict qualifier (like the register storage class) is to promote optimization [...]

However, I've heard about implementations (specifically found in embedded systems) where register has a stronger meaning: it is a command and the compiler shall place the qualified identifier in a register.

So, is an implementation allowed to follow that behavior and thus considered as standard-compliant? What would permit that?

I'm raising this question because I find that being obligated to place that object in a register is no longer suggestion, as mandated by the Standard; they conflict, in other words.

edmz
  • 8,220
  • 2
  • 26
  • 45
  • You omitted the first part of clause 6.7.3 which contains the word **required**, see a previous SO question http://stackoverflow.com/questions/26567078/usage-of-restrict-keyword – Weather Vane Mar 08 '15 at 16:22
  • @WeatherVane I'm referring about `register`, not `restrict` (I made a typo). – edmz Mar 08 '15 at 16:31
  • Why not? It's still a suggestion, but one that the compiler always respects. – marcus Mar 08 '15 at 16:54
  • You can always force something into a register with inline asm... which is a fairly common occurrence in embedded systems... outside of embedded, extended inline assembly is a little easier since you don't have to specify the register. Something like `register long variable_name __asm__("register") = value;` – technosaurus Mar 21 '15 at 06:35
  • Yes, but with inline asm *you* put it into a register and thus you fail. Otoh, if it's the implementation fault the program is anyways well-formed. – edmz Mar 21 '15 at 11:14

2 Answers2

3

It is allowed to do it as long as it doesn't prevent well-formed programs from compiling or affects the observed behavior as specified in the standard in any way.

The standard already forbids taking the address or alignment of an object declared with the register specifier, so that part wouldn't be a problem. A trickier case would be if you declare more objects with register than there are available registers. Unless the implementation still allows spilling for register objects (temporarily moving values from registers to e.g. the stack, and back), then this would be a case where the implementation would fail to compile a program that's conformant according to the standard.

Ulfalizer
  • 4,664
  • 1
  • 21
  • 30
  • May you add some references? – edmz Mar 08 '15 at 16:43
  • @black: All I did was to double-check what limitations the spec. imposes on `register` objects. It does not say that you can't have more `register` objects than there are registers available (the spec. is pretty abstract anyway and doesn't talk about registers, in order to not limit what kinds of machines C can be implemented on), so the implementation would have to be able to handle that if it wants to be conformant. – Ulfalizer Mar 08 '15 at 16:47
  • Well, doesn't talk about registers much anyway. :) – Ulfalizer Mar 08 '15 at 16:53
  • I'm mainly referring to your first sentence. It boils down to a requirement of something implementation-defined, doesn't it? That's why I was asking for quotes. – edmz Mar 08 '15 at 16:54
  • @black: The fact that you should be able to have more `register` objects than there are available registers does not seem to be implementation-defined. Implementation-defined behavior is "unspecified behavior where each implementation documents how the choice is made". The standard explicitly states what things are implementation-defined. – Ulfalizer Mar 08 '15 at 17:00
  • It's not always a deadly sin to not be standards-conforming though. The real world has practical concerns. If some compiler wants to guarantee that all `register` objects live in registers, e.g. because it makes things easier to reason about on some platform, then that might be just fine. The trade-off is that it might fail to compile some conforming programs. – Ulfalizer Mar 08 '15 at 17:07
  • "The trade-off is that it might fail to compile some conforming programs." Yeah, that's my point. In that case, the implementation would not be standard-compliant because, as you said, it would prevent well-formed programs to compile. Got it? – edmz Mar 08 '15 at 17:10
  • Yeah, that's what I've been saying. Sorry if I wasn't clear. :) – Ulfalizer Mar 08 '15 at 17:11
  • Great. Could you add this in your answer, to finalize it? – edmz Mar 08 '15 at 17:13
  • @black: Doesn't "unless the implementation still allows spilling for register objects (temporarily moving values from registers to e.g. the stack, and back), then this would be a case where the implementation would fail to compile a program that's conformant according to the standard" already cover it? If it doesn't allow that, then it's not standards-compliant (because it probably won't be able to compile some functions with more `register` objects than actual registers). – Ulfalizer Mar 08 '15 at 17:16
  • The C standard somewhat assumes that there is infinite memory. There is also an amount of stack variables where a program will probably crash when run, not only for register variables (of course, the limit is hit earlier with registers, but the standard doesn't address either of them, `malloc` can fail in a documented way, automatic allocation can't). – mafso Mar 08 '15 at 17:42
3

As you say, the standard says "The extent to which such suggestions are effective is implementation-defined."

That gives the implementation free range to do anything from ignoring the suggestion to moving heaven and earth to implement it. An implementation which chooses to accept the register specifier as requiring the use of a register is certainly not contradicting the standard, and nor is an implementation which just makes its own decisions about register placements regardless of specifiers.

The one thing the implementation should not do is refuse to compile a program because it would need to spill a register -- at least, up to the limits specified in §5.2.4.1 Translation limits -- but nothing stops the compiler from issuing a warning. (Nothing stops the compiler from issuing warnings about anything; it's common for compilers to warn about perfectly legal constructs which are considered dangerous.)

Edit: Rereading 5.2.4.1, it seems to me that an implementation could actually refuse to compile a program which it considers to have too many register specifiers, since the limits clause only binds the implementation to be able to translate and execute "one program" which includes (for example) "511 identifiers with block scope declared in one block", and not any program which does so. So as far as I can see, the compiler could insist that the "at least one program" which hits that limit not have any register specifications.

Note: Not all CPUs have registers in the common sense of the word, but the standard does not actually say anything about hardware. It simply says that the register specifier communicates the programmer's desire to make "access to the object be as fast as possible". Moreover, the compiler's attempt to satisfy that desire does not actually have to optimize access to the object; it's not a violation of the standard for optimization attempts to fail to optimize.

rici
  • 234,347
  • 28
  • 237
  • 341
  • "The one thing the implementation cannot do is refuse to compile a program because it would need to spill a register" that's because the program is, by the ISO, well-formed and would fail only due to the implementation's fault, doesn't it? – edmz Mar 08 '15 at 17:32
  • @black: 5.2.4.1 says "The implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following limits". That's not quite as strong as requiring it to translate and execute any program which doesn't exceed any limit, and one could argue that it doesn't preclude the implementation from restricting the "one program" to a program with no "register" declarations. So I'm going to soften my answer. – rici Mar 08 '15 at 17:47
  • To comment on the vagueness of the implementation limits (from the C99 Rationale V5.10 5.2.4.1): _While a deficient implementation could probably contrive a program that meets this requirement, yet still succeed in being useless, the C89 Committee felt that such ingenuity would probably require more work than making something useful._ So it seems that, technically, a conforming implementation could refuse to compile all programs but one. – mafso Mar 08 '15 at 17:57
  • @mafso: Yes, there are various lacunae in the standard, which are acceptable because most compiler-writers aren't actually malicious or perverse, at least in the creation of their compilers, at least in part because the compiler-writer typically wants people to use their compiler as a tool rather than as a counterexample. – rici Mar 08 '15 at 18:00
  • The spirit of the standard to me anyway is that `register` should only be a hint and not affect what compiles and runs and what doesn't (except for the limitations listed in the standard). I'd consider this a case where real-world concerns trump a strict reading. – Ulfalizer Mar 08 '15 at 18:01
  • @Ulfalizer: a strict reading of what? Strictly read, the standard doesn't limit the compiler at all, as per mafso's comment. – rici Mar 08 '15 at 18:02
  • @Ulfalizer: Then there is no need for real-world concerns to "trump" anything. The compiler acts according to what its authors consider real-world concerns. (Which may differ from yours, but you're not obliged to use the compiler.) – rici Mar 08 '15 at 18:04
  • We're probably talking past each other. The takeaway point is to do what's sensible for the particular compiler and platform, taking into account what programs might fail to run :). – Ulfalizer Mar 08 '15 at 18:10