Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the C compiler free to rewrite adjacent values in an array?

Tags:

arrays

c

volatile

Let's say I have the following code:

volatile char array[4];
array[0] = 1;

Now, ideally, what the compiler would do is convert this into an 8-bit store instruction so that only one byte is modified in memory. However, is it free to convert this to a read/modify/write? E.g., an ~equivalent way to handle this would be the following:

int32 *temp = (int*)array;
*temp = (*temp & 0xFFFFFF00) | 1;

The issue is obviously that the latter implementation will overwrite the other 3 bytes in the array. In a single-threaded application this is equivalent, but not in a multi-threaded case.

So is the compiler allowed to compile the first implementation the same as the second?

like image 765
JoshG79 Avatar asked Jul 16 '13 19:07

JoshG79


People also ask

Can you modify an array?

Arrays can be manipulated by using several actions known as methods. Some of these methods allow us to add, remove, modify and do lots more to arrays.

How do adjacent elements compare to arrays?

A Simple Approach is to traverse the given array one by one and compare every element with the given element 'x'. If matches, then return index. The above solution can be Optimized using the fact that the difference between all adjacent elements is at most k.


2 Answers

I think the answer is No; it violates the 'as if' rule. Because of the read involved in the rewrite, the code does not behave the same as the abstract model, especially since volatile is involved, so the rewrite would be invalid. Section 5.1.2.3 of the C11 standard applies:

5.1.2.3 Program execution

1 The semantic descriptions in this International Standard describe the behavior of an abstract machine in which issues of optimization are irrelevant.

2 Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects,12) which are changes in the state of the execution environment. Evaluation of an expression in general includes both value computations and initiation of side effects. Value computation for an lvalue expression includes determining the identity of the designated object.

...

4 In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

...

6 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 specified 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 the observable behavior of the program.

12) The IEC 60559 standard for binary floating-point arithmetic requires certain user-accessible status flags and control modes. Floating-point operations implicitly set the status flags; modes affect result values of floating-point operations. Implementations that support such floating-point state are required to regard changes to it as side effects — see annex F for details. The floating-point environment library <fenv.h> provides a programming facility for indicating when these side effects matter, freeing the implementations in other cases.

The wording in C99 is slightly simpler because it doesn't have to think about threads etc, but the gist of the wording is the same.

like image 131
Jonathan Leffler Avatar answered Nov 15 '22 00:11

Jonathan Leffler


First of all: with volatile, it's certainly not allowed in any C version -- it's treated as I/O (like putc).

Otherwise...

I believe it's valid for pre-C11, which doesn't have a threading model; it's a sequentially consistent transformation.

I suspect that for C11 and later, the answer would be the same as for C++11's threading model, which is a definite No because the threading model is SC-DRF: sequentially consistent, data-race-free.

Take a look at this video (12:36) -- Herb Sutter mentions this exact situation, and the answer is no for a valid SC-DRF compiler; a conforming C++11 compiler must never invent a write to a variable that would not have been written to.

Note that volatile is not necessary for this to be true.

If there is a compiler bug then that's a different story.

like image 28
user541686 Avatar answered Nov 15 '22 00:11

user541686