From: Enea Zaffanella <zaffanella@cs...>  20090828 16:01:14

Hello. > On Fri, 20090828 at 10:29 +0200, Benjamin Monate wrote: >> AFAIU if p==&a then >> a=++(*p) ; >> is the same as >> a=++a; >> which is undefined as it has two effects on "a" between two sequence points. > > The example had p==&p, not p==&a. a=++(*p) doesn't have two effects on > 'a'it has one on 'a' and one on 'p'. > >> See the answer of John Regher to have the normative reference. > > His email stated that the problem was that 'p' 'is modified and the > prior value is read other than to determine the value to be stored,' one > criterion for undefined behavior. > > Elnatan > I was considering the second example sent by Roberto. Here we need to evaluate the following: a = ((*p[0]>next) += 1); in a state where the subexpression *p[0]>next evaluates to p[0]. So the statement above modifies objects 'a' and 'p[0]', which are distinct. Moreover, the *old* value of object 'p[0]' is read (only once) and it is only used to compute the *new* value that has to be stored into object 'p[0]'. So I cannot see the undefined behavior mentioned in C99 6.52: ===== 2 Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.70) ===== To my eyes, this is exactly the same as p[0] = p[0] + 1; Am I missing something? Cheers, Enea Zaffanella. 
From: Enea Zaffanella <zaffanella@cs...>  20090901 17:07:37

Enea Zaffanella wrote: > Hello. [...] > I was considering the second example sent by Roberto. [...] > So I cannot see the undefined behavior mentioned in C99 6.52: Could a CIL developer confirm whether or not my observations are correct? That is, confirm whether or not this is a bug in CIL? Cheers, Enea Zaffanella. 
From: Gabriel Kerneis <kerneis@pp...>  20090901 18:46:18

Disclaimer: I am not an expert, did not write CIL (neither gcc ;), so what follows is nothing more than my humble understanding of C99. On Fri, Aug 28, 2009 at 06:00:40PM +0200, Enea Zaffanella wrote: > I was considering the second example sent by Roberto. > Here we need to evaluate the following: > > a = ((*p[0]>next) += 1); > > in a state where the subexpression > *p[0]>next > evaluates to > p[0]. > > So the statement above modifies objects 'a' and 'p[0]', > which are distinct. > Moreover, the *old* value of object 'p[0]' is read (only once) > and it is only used to compute the *new* value that has to be stored > into object 'p[0]'. > > So I cannot see the undefined behavior mentioned in C99 6.52: > ===== > 2 Between the previous and next sequence point an object shall have its > stored value modified at most once by the evaluation of an expression. > Furthermore, the prior value shall be read only to determine the value > to be stored. > ===== > > To my eyes, this is exactly the same as > > p[0] = p[0] + 1; > > Am I missing something? I'm not an expert, but the spec says an object is a "region of data storage in the execution environment, the contents of which can represent values" (3.14), so basically this is a segment of memory (and has few to do with symbolic variables from the original program). In this example, I think the object accessed via p[0] is NOT "read only to determine the value to be stored". It is also read to determine the value of *p[0]>next, to evaluate it to p[0], that is to determine where to store it. (This doesn't sound very convincing.) The fact that ++x is syntactic sugar for x += 1, and that += does evaluate it's left operand only once (so, this is NOT equivalent to x = x + 1) must also be important in that case (but I'm not sure what it means). On the other hand, I'm not sure CIL respects the "optimization rules for factoring out assignments" stated in the Rationale (6.5.16). I'm a bit lost here, to be perfectly honnest. And, further, the Rationale is not normative anyway... E.g., the above expression can be rewritten: a = ( t = (*p[0]>next), (*p[0]>next) = (*p[0]>next) + 1, t + 1 ); following the first rule, or a = ( t = (*p[0]>next) + 1, (*p[0]>next) = (*p[0]>next) + 1, t ); following the second rule (unless the "provided that neither i nor y have side effects themselves" provision is not met, but I don't see why they would have side effects). This is very deterministic, coherent with gcc, but not with CIL. Anyone else?  Gabriel Kerneis 
From: Enea Zaffanella <zaffanella@cs...>  20090902 17:42:47

Gabriel Kerneis wrote: > Disclaimer: I am not an expert, did not write CIL (neither gcc ;), so > what follows is nothing more than my humble understanding of C99. Hi Gabriel. > On Fri, Aug 28, 2009 at 06:00:40PM +0200, Enea Zaffanella wrote: >> I was considering the second example sent by Roberto. >> Here we need to evaluate the following: >> >> a = ((*p[0]>next) += 1); >> >> in a state where the subexpression >> *p[0]>next >> evaluates to >> p[0]. >> >> So the statement above modifies objects 'a' and 'p[0]', >> which are distinct. >> Moreover, the *old* value of object 'p[0]' is read (only once) >> and it is only used to compute the *new* value that has to be stored >> into object 'p[0]'. >> >> So I cannot see the undefined behavior mentioned in C99 6.52: >> ===== >> 2 Between the previous and next sequence point an object shall have its >> stored value modified at most once by the evaluation of an expression. >> Furthermore, the prior value shall be read only to determine the value >> to be stored. >> ===== >> >> To my eyes, this is exactly the same as >> >> p[0] = p[0] + 1; >> >> Am I missing something? > > I'm not an expert, but the spec says an object is a "region of data > storage in the execution environment, the contents of which can > represent values" (3.14), so basically this is a segment of memory (and > has few to do with symbolic variables from the original program). > > In this example, I think the object accessed via p[0] is NOT "read only > to determine the value to be stored". It is also read to determine the > value of *p[0]>next, to evaluate it to p[0], that is to determine where > to store it. (This doesn't sound very convincing.) Well, unfortunately, that piece of the standard is not crystal clear. I am not saying that I have properly understood its real meaning, I am just willing to reason with you to try and capture its real meaning. So your point is: if we are modifying an object, then its prior value should be used only to compute the new value of the same object; any other use of the prior value leads to undefined behavior. Let us assume that your reading is what was really meant in the standard. My goal is to provide some evidence that you were actually right when saying "(This doesn't sound very convincing.)" Consider the following example: int a[2] = { 1, 2 }; int i = 1; int j; a[i++] = 3; // Undefined behavior? In the third line, we modify two objects:  the integer at address &i (let us call it OBJ_1), whose prior value is 1, is incremented;  the integer (array element) at address &a[1] (let us call it OBJ_2), whose prior value 2 is not read, is assigned new value 3. The two objects OBJ_1 and OBJ_2 are distinct and do not overlap. The prior value of OBJ_1 has two uses: 1) compute the new value for OBJ_1; 2) compute the address of OBJ_2. Hence, by adopting your reading of the standard, the third line above should be flagged as undefined behavior. Do you think this is indeed a case of undefined behavior? If you think the answer is positive, then you can stop reading here. In my humble opinion the one above is not undefined behavior ... hence there should be another reading for what is (badly!) said in the standard. > The fact that ++x is syntactic sugar for x += 1, and that += does > evaluate it's left operand only once (so, this is NOT equivalent to > x = x + 1) must also be important in that case (but I'm not sure what it > means). My idea is that we will obtain undefined behavior if we have an expression that: a) reads the prior value of the modified object; and b) it is not used to compute the new value of the object. That is, there is no undefined behavior if *all* the expressions reading the prior value of the object are used to compute the new value of the object; but some of these can still be read for other uses. According to this interpretation of the standard, examples of undefined behavior are the following: a[i] = i = 0; a[i] = i++; Here, in both cases, the first occurrence of expression i tries to read the prior value of i, but it is not used to compute the new value of i. The rationale underlying my reading is that if all the expressions reading the prior value of the object are used to compute the new value, then they will be evaluated *before* assigning the new value to the object, hence they will all get the correct prior value. On the other hand, if there exists even a single expression reading the value of the object which is NOT used to compute its new value, then there will be no temporal dependency between the evaluation of this expression and the object update: the evaluation of the expression could take place before the side effect, after the side effect, or even during the execution of the side effect, leading to the undefined behavior. Does this sound as a reasonable reading of the standard? Going back to the original example: a = ((*p[0]>next) += 1); Here _all_ the (sub)expressions reading the prior value of the int* object found at address &p[0] are used (among other things) to compute the new value for the object. Hence, according to the interpretation above, there is no undefined behavior. Cheers, Enea. > On the other hand, I'm not sure CIL respects the "optimization rules for > factoring out assignments" stated in the Rationale (6.5.16). I'm a bit > lost here, to be perfectly honnest. And, further, the Rationale is not > normative anyway... > E.g., the above expression can be rewritten: > a = ( t = (*p[0]>next), (*p[0]>next) = (*p[0]>next) + 1, t + 1 ); > following the first rule, or > a = ( t = (*p[0]>next) + 1, (*p[0]>next) = (*p[0]>next) + 1, t ); > following the second rule (unless the "provided that neither i nor y > have side effects themselves" provision is not met, but I don't see why > they would have side effects). This is very deterministic, coherent with > gcc, but not with CIL. > > Anyone else? 