15

It's said that infinite loop for(;;); is undefined behavior.


From http://en.cppreference.com/w/cpp/language/memory_model

In a valid C++ program, every thread eventually does one of the following:

  • terminate
  • makes a call to an I/O library function
  • reads or modifies a volatile object
  • performs an atomic operation or a synchronization operation

No thread of execution can execute forever without performing any of these observable behaviors.

Note that it means that a program with endless recursion or endless loop (whether implemented as a for-statement or by looping goto or otherwise) has undefined behavior.


But what if it calls a function in shared library?

for(;;) sofunc();

The function could do any kind of blocking I/O, or throw exception.

In this case, does the compiler assume that the loop has some observable behaviors?

cshu
  • 4,814
  • 23
  • 36
  • 2
    if `sofunc` doesn't do one of the specified operations, you have invalid program. – Jarod42 Dec 25 '16 at 11:31
  • 1
    I don't know any compiler, OS or CPU that cause any trouble by either using empty non ending loop or non ending loop that calls a function. outside the standard world this question is ridiculous. I have no claims for the OP,I just think this is not a real issue – David Haim Dec 25 '16 at 11:31
  • But isn't CALL an atomic instruction? – Logman Dec 25 '16 at 12:29
  • 2
    @DavidHaim -- undefined behavior means **only** that the language definition doesn't tell you what I he program should do. It's not ridiculous to ask about the limits that apply. – Pete Becker Dec 25 '16 at 12:43
  • Compilers are not required to detect undefined behavior. – brian beuning Dec 25 '16 at 12:46
  • I would say the conclusion is incorrect. It is surely only UB if the program loops indefinitely without hitting one of the four actions lists. – user207421 Dec 25 '16 at 15:55
  • 1
    A function call may not be an atomic operation and depends on the architecture of the processor, as well as the parameter count. Look at the assembly count. For example, a common method is to load parameters into registers then perform a "call" instruction. The "call" instruction may cause the processor to reload its instruction cache or pipeline. I don't see how this process can be atomic. – Thomas Matthews Dec 25 '16 at 18:23
  • 2
    An infinite loop is well defined behavior. Transferring execution to a function and returning is well defined behavior. – Thomas Matthews Dec 25 '16 at 18:25
  • 1
    The compiler can only determine the behavior of the loop based on the declaration of the function. If the function is not within the same translation unit, the **compiler** cannot determine any behaviors of an external function. The **linker** is primarily involved in resolving symbols and maybe generation of machine code. Most linkers do not analyze code in libraries, but uses them. Operating Systems may have the responsibility of managing calls to dynamic libraries (such as loading them into memory if necessary). – Thomas Matthews Dec 25 '16 at 18:30
  • 2
    Shared libraries are not covered by the standard, so the standard says nothing about them. – Raymond Chen Dec 25 '16 at 20:55
  • An implementation, without being able to see the body of `sofunc`, can assume that it will eventually make some progress. This allows the implementation, in principle, e.g. to stop all other threads and wait until such progress is actually made. – n. 'pronouns' m. Dec 25 '16 at 22:24
  • @DavidHaim we have an example here in [C Compilers Disprove Fermat’s Last Theorem](http://blog.regehr.org/archives/140). We have many examples of UB that get optimized in odd places for example [this case](http://stackoverflow.com/q/32506643/1708801) and [this case](http://stackoverflow.com/q/24296571/1708801) show some surprising optimizations. [Is uninitialized local variable the fastest random number generator?](http://stackoverflow.com/q/31739792/1708801) for an example of the dangers of inconsistent or morphing optimizations wrt to UB. – Shafik Yaghmour Jan 03 '17 at 20:13
  • @DavidHaim also see [Compilers and Termination Revisited](http://blog.regehr.org/archives/161) for a list of compilers known not to preserve infinite loops. – Shafik Yaghmour Jan 03 '17 at 21:55
  • See related question: [Optimizing away a “while(1);” in C++0x](http://stackoverflow.com/q/3592557/1708801), some of the comments are relevant here. Also see the [C version of the question](http://stackoverflow.com/q/2178115/1708801). – Shafik Yaghmour Jan 03 '17 at 21:58
  • Distantly similar [Why is a `fork()` call not optimized away in an infinite loop?](http://stackoverflow.com/questions/29295427/why-is-a-fork-call-not-optimized-away-in-an-infinite-loop/29296483) – Mohit Jain Jan 04 '17 at 10:25
  • The last paragraph of the quote is poorly worded IMO; an endless loop does *not* have UB if the loop also produces observable behaviour – M.M Jul 24 '17 at 00:26

1 Answers1

8

There are a number of places where the language of the Standard gives compilers freedoms beyond what are required for useful optimizations, but which would instead provide compilers with ways to throw the Principle of Least Astonishment out the window. The way the rules about endless loops are written fits that category.

Most of the optimizations which would be facilitated by the rules about endless loops would be enabled by language which specified that the time required to execute a section of code, even if infinite, is not considered to be a side-effect that compilers are required to preserve. Such a rule would allow a compiler to omit any loop iterations which don't have any direct side-effects and don't modify values that are used elsewhere.

The Standard, however, goes beyond that. Given the code:

int foo(void)
{
  int x=0;
  do
  {
    x=functionWithNoSideEffects(x);
  } while(x != 23 && x != 42);
  return x;
}

a compiler that could show that functionWithNoSideEffects would never have any defined side-effects and would never return 23 could replace the code for "foo" with "return 42;". Even if the purpose of the program was to test whether functionWithNoSideEffects would ever return 42 (in which case having the generated code return 42 whether the function does or not would be unhelpful) the Standard would not require compilers to generate code to actually test that unless the loop included some kind of "side-effect".

I'm not personally convinced that the value of having a rule that says that if compilers can show that a loop can't terminate without X being true, they may regard X as being true whether or not there's any means by which it could be. Optimizations based on that principle seem popular, however.

supercat
  • 69,493
  • 7
  • 143
  • 184