0

I have been researching this topic and I can not find a specific authoritative answer. I am hoping that someone very familiar with the C spec can answer - i.e. confirm or refute my assertion, preferably with citation to the spec.

Assertion: If a program consists of more than one compilation unit (separately compiled source file), the compiler must assure that global variables (if modified) are written to memory before any call to a function in another unit or before the return from any function. Also, in any function, the global must be read before its first use. Also after a call of any function, not in the same unit, the global must be read before use. And these things must be true whether the variable is qualified as "volatile" or not because a function in another compilation unit (source file) could access the variable without the compiler's knowledge. Otherwise, "volatile" would always be required for global variables - i.e. non-volatile globals would have no purpose.

Could the compiler treat functions in the same compilation unit differently than ones that aren't? All of the discussions I have found for the "volatile" qualifier on globals show all functions in the same compilation unit.

Edit: The compiler cannot know whether functions in other units use the global or not. Therefore I am assuming the above conditions.

I found these two other questions with information related to this topic but they don't address it head on or they give information that I find suspect:

Are global variables refreshed between function calls?

When do I need to use volatile in ISRs?

Community
  • 1
  • 1
PaulB
  • 128
  • 8
  • Why must the compiler make sure a global variable is written to memory before a function returns? This is really up to the CPU core and its cache-handling. – Some programmer dude Sep 09 '16 at 17:01
  • Both of the links you provided state that without `volatile` there is no guarantee that the variable will ever be read from memory. – Riley Sep 09 '16 at 17:05
  • @Riley, I know that is what they say. For the reasons I state, I believe that they must be wrong. – PaulB Sep 09 '16 at 17:07
  • @Joachim, my question refers to the C "virtual machine" and hardware memory cache does not play a part. – PaulB Sep 09 '16 at 17:28
  • @PaulB C doesn't use a virtual machine. – Riley Sep 09 '16 at 17:39
  • The compiler has to be able to guarantee that the variable will still have the value that this program assigned to it to be able to make any optimizations. Therefore, if another function could change the value, then the value has to be somewhere that it can be changed (in memory, or in a register). If something outside the program changes the value in memory, then the change might not be reflected. – Riley Sep 09 '16 at 18:03
  • @Riley, ok, no virtual machine (if you say so), then I mean whatever abstraction it does use that is independent of specific hardware. – PaulB Sep 09 '16 at 18:25
  • @Riley, I said nothing about anything outside of the program. I don't understand the point of your comment. A single C program can be compiled in pieces and linked together later. This is very common. The compiler can assume that the functions will execute one at a time but it cannot know what the functions do unless they are compiled at the same time - therefore it must assume the worst. – PaulB Sep 09 '16 at 18:32
  • @PaulB The only "abstraction" is that the machine level instructions will do the same thing on the same architecture, but we're getting off topic... – Riley Sep 09 '16 at 18:35
  • @Riley C certainly has a "virtual" (the correct term is "abstract") machine ... or better semantics. That doesn't mean it's running with byte code or anything like that, though. (But an implementation could) – Daniel Jour Sep 09 '16 at 18:36
  • @PaulB You're right, it must assume the worst, but that doesn't mean that is must read/write to memory before/after all function. Daniel Jour's answer explains it pretty well. – Riley Sep 09 '16 at 18:38
  • @DanielJour There is an abstract machine concept, but it isn't like the Java virtual machine. When I first read the comment it sounded like that is what Paul was talking about. On a second look, he could have been talking about the theoretical abstract machine that C uses. – Riley Sep 09 '16 at 18:45
  • @Riley, Daniel Jour does not address the the problem of functions compiled separately. I think I have misdirected the discussion with use of the word, "must" and with my assumption that C would treat all functions the same regardless of compilation unit. I will see if I can edit the question to better reflect my intention. – PaulB Sep 09 '16 at 18:49
  • This may not always be true, but I compiled (didn't link) a file that called a function that wasn't implemented, then used a global variable afterwards. In the assembly, it appears to call the function then load the global before using it again. – Riley Sep 09 '16 at 19:35

1 Answers1

1

[..] in any function, the global must be read before its first use.

Definitely not:

static int variable;
void foo(void) {
  variable = 42;
}

Why should the compiler bother generating code to read the variable?

The compiler must assure that global variables are written to memory before any function call or before the return from a function.

No, why should it?

void bar(void) {
   return;
}
void baz(void) {
  variable = 42;
  bar();
}

bar is a pure function (should be determinable for a decent compiler), so there's no chance of getting any different behaviour when writing to memory after the function call.

The case of "before returning from a function" is tricky, though. But I think the general statement ("must") is false if we count inlined (static) functions, too.

Could the compiler treat functions in the same compilation unit differently than ones that aren't?

Yes, I think so: for a static function (whose address is never taken) the compiler knows exactly how it is used, and this information could be used to apply some more radical optimisations.

I'm basing all of the above on the C version of the As-If rule, specified in §5.1.2.3/6 (N1570):

The least requirements on a conforming implementation are:

  • Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.

  • At program termination, all data written into files shall be identical to the result that execution of the program according to the abstract semantics would have produced.

  • The input and output dynamics of interactive devices shall take place as specied in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input.

This is theobservable behaviorof the program.

In particular, you might want to read the following "EXAMPLE 1".

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • I guess I provoked the wrong response by my use of the word, "must". I was really more interested in the more common case where there are multiple compilation units. And I probably wrongly assumed that C would treat all functions in a common way; but, I acknowledge that may not be true. – PaulB Sep 09 '16 at 18:44
  • How does the compiler know that a function in another compilation unit does not call "bar" or "baz"? And how does the compiler of that other unit know that "baz" or "bar" doesn't modify or use "variable"? I think it might even be possible that the linker could substitute a different version of "baz" and/or "bar" at a later time. – PaulB Sep 09 '16 at 19:16