Message ID | 4C1F5380.1090107@codesourcery.com |
---|---|
State | New |
Headers | show |
On Mon, Jun 21, 2010 at 1:56 PM, Nathan Sidwell <nathan@codesourcery.com> wrote: > I've come across inconsistent and surprising behaviour when assigning to > volatile lvalues. > > Sometimes we re-read the assigned-to object, and sometimes we do not. For > instance, > return vobj = data; > will cause a reread of vobj, IF data is not a constant. > or > cond ? vobj = data : 0 > will cause a reread of vobj, even when we don't use the result of the > conditional expression -- unless data is a constant, in which case there is > no-reread. (And confusingly, if you put void casts inside the two > conditional sub-expressions, the re-reading goes away, but it doesn't even > when you explicitly cast the whole expression to void). > > In the attached testcase, test_1, test_6 and test_7 have this surprising > rereading behaviour. > > The fault appears to be in gimplify_modify_expr, where we return the LHS as > the value, if the caller wants the value. The attached patch changes that > routine when the target lvalue is volatile. In that case it will create a > temporary to hold the RHS value, assign that temporary to the LHS and then > return the temporary. > > Although the bug is architecture-neutral, the testcase is x86-specific > because it's checking for specific accesses occurring. > > built and tested on i686-pc-linux-gnu, ok? Hm, TREE_THIS_VOLATILE (TREE_TYPE (*to_p)) should certainly be TREE_THIS_VOLATILE (*to_p), and all this should be done after gimplifying *to_p. I think even just before we build the gimple assign/call do if (TREE_THIS_VOLATILE (*to_p) && want_value) *from_p = get_initialized_tmp_var (*from_p, pre_p, post_p); and change if (want_value) { *expr_p = unshare_expr (*to_p); return GS_OK; to if (want_value) { *expr_p = TREE_THIS_VOLATILE (*to_p) ? *from_p : unshare_expr (*to_p); return GS_O; Richard.
Hello, On Mon, 21 Jun 2010, Richard Guenther wrote: > > I've come across inconsistent and surprising behaviour when assigning > > to volatile lvalues. > > > > Sometimes we re-read the assigned-to object, and sometimes we do not. For > > instance, > > return vobj = data; > > will cause a reread of vobj, IF data is not a constant. I'd consider the latter condition a bug. I.e. the rereading must happen in every case. It must happen because in C the value of "lhs = rhs" is that of reading 'lhs' ("An assignment expression has the value of the left operand after the assignment, but is not an lvalue." 6.5.16), and if lhs happens to be volatile than the abstract machine implies two accesses to lhs, one write (of the assignment) and one read (to get to the value of lhs). > > or > > cond ? vobj = data : 0 > > will cause a reread of vobj, even when we don't use the result of the > > conditional expression -- unless data is a constant, in which case there is > > no-reread. (And confusingly, if you put void casts inside the two > > conditional sub-expressions, the re-reading goes away, but it doesn't even > > when you explicitly cast the whole expression to void). > > > > In the attached testcase, test_1, test_6 and test_7 have this surprising > > rereading behaviour. > > > > The fault appears to be in gimplify_modify_expr, where we return the LHS as > > the value, if the caller wants the value. The attached patch changes that > > routine when the target lvalue is volatile. In that case it will create a > > temporary to hold the RHS value, assign that temporary to the LHS and then > > return the temporary. ... which implies that I think that this is exactly the wrong solution. Ciao, Michael.
On Mon, Jun 21, 2010 at 3:30 PM, Michael Matz <matz@suse.de> wrote: > Hello, > > On Mon, 21 Jun 2010, Richard Guenther wrote: > >> > I've come across inconsistent and surprising behaviour when assigning >> > to volatile lvalues. >> > >> > Sometimes we re-read the assigned-to object, and sometimes we do not. For >> > instance, >> > return vobj = data; >> > will cause a reread of vobj, IF data is not a constant. > > I'd consider the latter condition a bug. I.e. the rereading must happen > in every case. It must happen because in C the value of "lhs = rhs" is > that of reading 'lhs' ("An assignment expression has the value of the left > operand after the assignment, but is not an lvalue." 6.5.16), and if lhs > happens to be volatile than the abstract machine implies two accesses to > lhs, one write (of the assignment) and one read (to get to the value of > lhs). > >> > or >> > cond ? vobj = data : 0 >> > will cause a reread of vobj, even when we don't use the result of the >> > conditional expression -- unless data is a constant, in which case there is >> > no-reread. (And confusingly, if you put void casts inside the two >> > conditional sub-expressions, the re-reading goes away, but it doesn't even >> > when you explicitly cast the whole expression to void). >> > >> > In the attached testcase, test_1, test_6 and test_7 have this surprising >> > rereading behaviour. >> > >> > The fault appears to be in gimplify_modify_expr, where we return the LHS as >> > the value, if the caller wants the value. The attached patch changes that >> > routine when the target lvalue is volatile. In that case it will create a >> > temporary to hold the RHS value, assign that temporary to the LHS and then >> > return the temporary. > > ... which implies that I think that this is exactly the wrong solution. And also implies that the frontend should arrange for what gets used. Richard. > > Ciao, > Michael.
On 06/21/10 14:30, Michael Matz wrote: > I'd consider the latter condition a bug. I.e. the rereading must happen > in every case. It must happen because in C the value of "lhs = rhs" is > that of reading 'lhs' ("An assignment expression has the value of the left > operand after the assignment, but is not an lvalue." 6.5.16), and if lhs > happens to be volatile than the abstract machine implies two accesses to > lhs, one write (of the assignment) and one read (to get to the value of > lhs). The subexpression 'vobj = expr' has a value. Shouldn't there therefore be a reread of that value in the expression-statement 'vobj = expr;'? nathan
On 21 Jun 2010, at 14:30, Michael Matz wrote: > On Mon, 21 Jun 2010, Richard Guenther wrote: > >>> I've come across inconsistent and surprising behaviour when >>> assigning >>> to volatile lvalues. >>> >>> Sometimes we re-read the assigned-to object, and sometimes we do >>> not. For >>> instance, >>> return vobj = data; >>> will cause a reread of vobj, IF data is not a constant. > > I'd consider the latter condition a bug. I.e. the rereading must > happen > in every case. It must happen because in C the value of "lhs = rhs" > is > that of reading 'lhs' ("An assignment expression has the value of > the left > operand after the assignment, but is not an lvalue." 6.5.16), and if > lhs > happens to be volatile than the abstract machine implies two > accesses to > lhs, one write (of the assignment) and one read (to get to the value > of > lhs). that answers the "what" but not the "when". Suppose that vobj was a hardware register ... ... I would expect that the assignment would write the value of expr to the register... ... but I would _not_ expect the register to be re-read (and, indeed, in many cases in hardware interaction - that could cause an issue). or did I miss sth? Iain
Hi, On Mon, 21 Jun 2010, Nathan Sidwell wrote: > On 06/21/10 14:30, Michael Matz wrote: > > > I'd consider the latter condition a bug. I.e. the rereading must happen > > in every case. It must happen because in C the value of "lhs = rhs" is > > that of reading 'lhs' ("An assignment expression has the value of the left > > operand after the assignment, but is not an lvalue." 6.5.16), and if lhs > > happens to be volatile than the abstract machine implies two accesses to > > lhs, one write (of the assignment) and one read (to get to the value of > > lhs). > > The subexpression 'vobj = expr' has a value. Shouldn't there therefore be a > reread of that value in the expression-statement 'vobj = expr;'? A good question. I'm very certain that we need a reread for "x = (vobj = y);" . (I even thought we had testcases for this actually happening) I would find the consequence of also having to reread for "vobj = y;" surprising. There's some leeway in the standard as to what actually constitutes an access to a volatile object (implementation-defined), and perhaps we can define it to be no (read) access in the second case: The subexpression has a value, but the source code doesn't retrieve it, and I think we're free to define this non-retrieval to imply no access to the value (and hence to vobj). Ciao, Michael.
On Mon, Jun 21, 2010 at 4:07 PM, Michael Matz <matz@suse.de> wrote: > Hi, > > On Mon, 21 Jun 2010, Nathan Sidwell wrote: > >> On 06/21/10 14:30, Michael Matz wrote: >> >> > I'd consider the latter condition a bug. I.e. the rereading must happen >> > in every case. It must happen because in C the value of "lhs = rhs" is >> > that of reading 'lhs' ("An assignment expression has the value of the left >> > operand after the assignment, but is not an lvalue." 6.5.16), and if lhs >> > happens to be volatile than the abstract machine implies two accesses to >> > lhs, one write (of the assignment) and one read (to get to the value of >> > lhs). >> >> The subexpression 'vobj = expr' has a value. Shouldn't there therefore be a >> reread of that value in the expression-statement 'vobj = expr;'? > > A good question. I'm very certain that we need a reread for > "x = (vobj = y);" . > (I even thought we had testcases for this actually happening) > I would find the consequence of also having to reread for "vobj = y;" > surprising. > > There's some leeway in the standard as to what actually constitutes an > access to a volatile object (implementation-defined), and perhaps we can > define it to be no (read) access in the second case: The subexpression has > a value, but the source code doesn't retrieve it, and I think we're free > to define this non-retrieval to imply no access to the value (and hence to > vobj). The standard also specifies that the value has unqualified type (whether that matters here or not I am not sure). So x is assigned from a non-volatile typed value. Richard.
Hi, On Mon, 21 Jun 2010, IainS wrote: > > > > return vobj = data; > > > >will cause a reread of vobj, IF data is not a constant. > > > >I'd consider the latter condition a bug. I.e. the rereading must happen > >in every case. It must happen because in C the value of "lhs = rhs" is > >that of reading 'lhs' ("An assignment expression has the value of the left > >operand after the assignment, but is not an lvalue." 6.5.16), and if lhs > >happens to be volatile than the abstract machine implies two accesses to > >lhs, one write (of the assignment) and one read (to get to the value of > >lhs). > > that answers the "what" but not the "when". > > Suppose that vobj was a hardware register ... > > ... I would expect that the assignment would write the value of expr to the > register... > > ... but I would _not_ expect the register to be re-read (and, indeed, in many > cases in hardware interaction - that could cause an issue). > > or did I miss sth? Yes, namely that the author explicitely wrote a read access to vobj. To demonstrate, like return vobj; implies a read from vobj, also return vobj = x; implies a read from vobj. The latter _also_ implies a store to vobj. If you want the read to not happen the author better writes vobj = x; return x; Ciao, Michael.
On 06/21/10 15:09, Michael Matz wrote: > Yes, namely that the author explicitely wrote a read access to vobj. > To demonstrate, like > return vobj; > implies a read from vobj, also > return vobj = x; > implies a read from vobj. The latter _also_ implies a store to vobj. If > you want the read to not happen the author better writes > vobj = x; > return x; Then what about plain vobj; ? Is that a read of vobj? what about hiding the assignment inside a cond expr cond ? vobj = expr : 0; Is that a re-read of vobj? nathan
Hi, On Mon, 21 Jun 2010, Richard Guenther wrote: > >> The subexpression 'vobj = expr' has a value. Shouldn't there > >> therefore be a reread of that value in the expression-statement 'vobj > >> = expr;'? > > > > A good question. I'm very certain that we need a reread for "x = > > (vobj = y);" . (I even thought we had testcases for this actually > > happening) I would find the consequence of also having to reread for > > "vobj = y;" surprising. > > > > There's some leeway in the standard as to what actually constitutes an > > access to a volatile object (implementation-defined), and perhaps we can > > define it to be no (read) access in the second case: The subexpression has > > a value, but the source code doesn't retrieve it, and I think we're free > > to define this non-retrieval to imply no access to the value (and hence to > > vobj). > > The standard also specifies that the value has unqualified type > (whether that matters here or not I am not sure). So x is assigned > from a non-volatile typed value. Yes, but I don't think this matters. In 'int x = (int)vobj;' the value of the RHS will also be unqualified, but to get to that value you still have to access vobj. Ciao, Michael.
On 06/21/10 15:09, Michael Matz wrote: > implies a read from vobj, also > return vobj = x; > implies a read from vobj. Also, doesn't that contradict the sequence-point rules? We'd have a read and write of vobj without an intervening sequence point (and the read was not used to determine the to-be-written value). nathan
Hi, On Mon, 21 Jun 2010, Nathan Sidwell wrote: > > Yes, namely that the author explicitely wrote a read access to vobj. > > To demonstrate, like > > return vobj; > > implies a read from vobj, also > > return vobj = x; > > implies a read from vobj. The latter _also_ implies a store to vobj. If > > you want the read to not happen the author better writes > > vobj = x; > > return x; > > Then what about plain > vobj; > ? Is that a read of vobj? As said, implementation-defined I think. (I think we should define it to be no access, unlike 'x = vobj;' even if x is unused later.). > what about hiding the assignment inside a cond expr > cond ? vobj = expr : 0; > Is that a re-read of vobj? If that's the full expression I think we should say that this doesn't constitute a read access. If the above was itself only a subexpression it should be a read access, like in x = cond ? vobj = expr : 0; or f (cond ? vobj = expr : 0); (Of course both accesses would only happen if cond is true). Put probably Joseph will correct me on all points I've made :-) Ciao, Michael.
Hi, On Mon, 21 Jun 2010, Nathan Sidwell wrote: > > implies a read from vobj, also > > return vobj = x; > > implies a read from vobj. > > Also, doesn't that contradict the sequence-point rules? We'd have a > read and write of vobj without an intervening sequence point (and the > read was not used to determine the to-be-written value). I think there's no problem. We have three side-effects for the above expression: two accesses to vobj (one read (1), one write (2)), and the modification of object vobj (3). The separation of the volatile write access and the actual modification of value may seem academic, but let's follow the letter. All these side-effects must be complete before the next sequence point (we have only one here, the end of full expression), which will obviously be the case, no matter if there's side-effect (1) or not. Now, onto 6.5 #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) This talks only about the side-effect of changing the stored value (which we do only once) and about the prior value, which we don't use at all. I.e. I think volatility doesn't enter the picture as far as 6.5. #2 is concerned, it rules out the usual pattern of "i=i++ + 1", no matter if i is volatile or not. Ciao, Michael.
On 21/06/2010 15:25, Michael Matz wrote: > Hi, > > On Mon, 21 Jun 2010, Nathan Sidwell wrote: > >>> Yes, namely that the author explicitely wrote a read access to vobj. >>> To demonstrate, like >>> return vobj; >>> implies a read from vobj, also >>> return vobj = x; >>> implies a read from vobj. The latter _also_ implies a store to vobj. If >>> you want the read to not happen the author better writes >>> vobj = x; >>> return x; >> Then what about plain >> vobj; >> ? Is that a read of vobj? > > As said, implementation-defined I think. (I think we should define it to > be no access, unlike 'x = vobj;' even if x is unused later.). I'm not so sure, that's how people write code to read-and-discard auto-resetting flag registers in memory mapped hardware. cheers, DaveK
Michael Matz wrote: >>> Sometimes we re-read the assigned-to object, and sometimes we do not. For >>> instance, >>> return vobj = data; >>> will cause a reread of vobj, IF data is not a constant. > > I'd consider the latter condition a bug. I.e. the rereading must happen > in every case. I think that it's without question that: vobj = data; should not cause a read from vobj; the value is not used. I think real-world embedded programmers would be surprised to see a read from an I/O register there. Do you agree? Similarly, I think that: cond ? vobj = data : ... should not cause a read from vobj. The conditional in this case is just a form of if/then; it would surprise programmers if this behaved differently from: if (cond) vobj = data; Do you agree? The case of: x = vobj = data; is different because the value is being used. In that case, I have no strong opinion. Rather than argue about this last case in theory, though, I'd be inclined to look at existing practice. What do RealView, CodeWarrior, Green Hills or other long-standing compilers for embedded systems do? If there's a clear consensus, we can follow that.
On Tue, Jun 22, 2010 at 11:17:31AM -0400, Mark Mitchell wrote: > if (cond) > vobj = data; > > Do you agree? > > The case of: > > x = vobj = data; > > is different because the value is being used. In that case, I have no > strong opinion. What about (void) vobj; or (void) (vobj = data); ? Should that read vobj? Jakub
Hi, On Tue, 22 Jun 2010, Mark Mitchell wrote: > I think that it's without question that: > > vobj = data; > > should not cause a read from vobj; the value is not used. I think > real-world embedded programmers would be surprised to see a read from an > I/O register there. Do you agree? Yes. > cond ? vobj = data : ... > > should not cause a read from vobj. > > Do you agree? Yes. > The case of: > x = vobj = data; > > is different because the value is being used. In that case, I have no > strong opinion. > > Rather than argue about this last case in theory, though, I'd be > inclined to look at existing practice. What do RealView, CodeWarrior, > Green Hills or other long-standing compilers for embedded systems do? That would be interesting, yes. > If there's a clear consensus, we can follow that. Ciao, Michael.
> Michael Matz wrote: > > >>> Sometimes we re-read the assigned-to object, and sometimes we do > not. For > >>> instance, > >>> return vobj = data; > >>> will cause a reread of vobj, IF data is not a constant. > > > > I'd consider the latter condition a bug. I.e. the rereading must > happen > > in every case. > > I think that it's without question that: > > vobj = data; > > should not cause a read from vobj; the value is not used. I think > real-world embedded programmers would be surprised to see a read from > an > I/O register there. Do you agree? Yes. Whatever else you do, this property is mandatory. > Similarly, I think that: > > cond ? vobj = data : ... > > should not cause a read from vobj. The conditional in this case is > just > a form of if/then; it would surprise programmers if this behaved > differently from: > > if (cond) > vobj = data; > > Do you agree? Maybe. Personally I would view the statement you listed as bad code. > The case of: > > x = vobj = data; > > is different because the value is being used. In that case, I have no > strong opinion. > > Rather than argue about this last case in theory, though, I'd be > inclined to look at existing practice. What do RealView, CodeWarrior, > Green Hills or other long-standing compilers for embedded systems do? > If there's a clear consensus, we can follow that. Right. The question on the statement you quoted is whether people interpret it as "data is assigned to two variables" or "data is assigned to vobj and the vobj is assigned to x". I take it from earlier discussion that the standard says the latter. Actually, given that this code is confusing, it might deserve a warning. Finally, whatever is done must produce the same answer whether the code is compiled as C or C++. paul
> What about > (void) vobj; > or > (void) (vobj = data); > ? Should that read vobj? Yes on the first. Don't know on the second. The problem is that some programmers have a bad habit of throwing in (void) when it isn't needed, for example when ignoring the return value of a function used as a statement. Code like (void) printf (...) is quite common. Given that people do this, it's hard to guess what (void) (a=b); was intended to convey. Maybe the best thing to do with that one is (a) pick an answer, (b) generate a warning. paul
Michael Matz wrote: >> x = vobj = data; >> Rather than argue about this last case in theory, though, I'd be >> inclined to look at existing practice. What do RealView, CodeWarrior, >> Green Hills or other long-standing compilers for embedded systems do? > That would be interesting, yes. Nathan, I know that we have RealView available for testing, and I believe we also have CodeWarrior installed. Would you like to give those two a try? If so, you could also look at: vobj; and: (void) vobj; I think those should do reads, but, again, I'd be persuaded by existing practice.
On 06/22/10 16:36, Mark Mitchell wrote: > Nathan, I know that we have RealView available for testing, and I > believe we also have CodeWarrior installed. Would you like to give > those two a try? I couldn't get CodeWarrior to play. RVCT generated code consistent with my proposal. Specifically in: some_use_of = volatile_object = value; the volatile object is only written to and not read, regardless of the context of that volatile assignment (be it return expression, conditional expression or serial assignment). > vobj; > (void) vobj; Both of these cause a read of vobj in RVCT (and GCC). nathan
Nathan Sidwell wrote: > RVCT generated code consistent with my proposal. Specifically in: > some_use_of = volatile_object = value; > the volatile object is only written to and not read, regardless of the > context of that volatile assignment (be it return expression, > conditional expression or serial assignment). Good. >> vobj; >> (void) vobj; > > Both of these cause a read of vobj in RVCT (and GCC). Good. If there are no objections within the next 48 hours, then I think we should consider the semantics settled. I'm not trying to squash discussion; if people don't like those semantics, let's discuss. But, I think existing practice should weigh heavily. Thanks,
Hi, On Wed, 23 Jun 2010, Mark Mitchell wrote: > >> vobj; > >> (void) vobj; > > > > Both of these cause a read of vobj in RVCT (and GCC). > > Good. > > If there are no objections within the next 48 hours, then I think we > should consider the semantics settled. I'm not trying to squash > discussion; if people don't like those semantics, let's discuss. But, I > think existing practice should weigh heavily. FWIW I agree. Ciao, Michael.
On Wed, Jun 23, 2010 at 3:10 PM, Michael Matz <matz@suse.de> wrote: > Hi, > > On Wed, 23 Jun 2010, Mark Mitchell wrote: > >> >> vobj; >> >> (void) vobj; >> > >> > Both of these cause a read of vobj in RVCT (and GCC). >> >> Good. >> >> If there are no objections within the next 48 hours, then I think we >> should consider the semantics settled. I'm not trying to squash >> discussion; if people don't like those semantics, let's discuss. But, I >> think existing practice should weigh heavily. > > FWIW I agree. And thus the revised patch is ok if it indeed implements the above and passes bootstrap & regtest. Thanks, Richard. > > Ciao, > Michael. >
> Nathan Sidwell wrote: > > > RVCT generated code consistent with my proposal. Specifically in: > > some_use_of = volatile_object = value; > > the volatile object is only written to and not read, regardless of > the > > context of that volatile assignment (be it return expression, > > conditional expression or serial assignment). > > Good. > > >> vobj; > >> (void) vobj; > > > > Both of these cause a read of vobj in RVCT (and GCC). > > Good. > > If there are no objections within the next 48 hours, then I think we > should consider the semantics settled. I'm not trying to squash > discussion; if people don't like those semantics, let's discuss. But, > I > think existing practice should weigh heavily. Agreed. The semantics will be the same for C and C++, correct? paul
Paul Koning wrote:
> The semantics will be the same for C and C++, correct?
I should certainly hope so. I see no reason for them to be different,
and we certainly want to minimize such differences.
On Jun 23, 2010, at 6:08 AM, Mark Mitchell wrote: > Nathan Sidwell wrote: > >> RVCT generated code consistent with my proposal. Specifically in: >> some_use_of = volatile_object = value; >> the volatile object is only written to and not read, regardless of the >> context of that volatile assignment (be it return expression, >> conditional expression or serial assignment). > > Good. > >>> vobj; >>> (void) vobj; >> >> Both of these cause a read of vobj in RVCT (and GCC). > > Good. > > If there are no objections within the next 48 hours, I object. gcc semantics are carefully designed. gcc is the standard compiler for much embedded work, C and C++ differ due to the standards organizations, some of those differences are truly unfortunate, but there you go. If someone wanted to fix gcc, the proper way would be to get the C and C++ committees on the same page, nail it down, or make it a hard error, and remove any latitude and confusion and make them agree. C++ says: All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand. You can't just willy nilly change this: int i, j, k; void foo(int &j) { foo(i = k); } can sense if you get it right or not. Add a volatile on i, and you discover, you can't just change this as you see fit, in particular, not in the way you all seem to want, which is just plain wrong. Further, C says: An assignment expression has the value of the left operand after the assignment Now, this is pretty plain, the value is the left operand. They choose this wording as they didn't want the return value to be a modifiable lvalue. I concede there is enough wiggle room here to confuse users and give implementors enough latitude to try and claim they are following the standard, but really, the return value is the valuer of the left operand, if it weren't, the standard would not say it was. It could have said, the value is the value that was stored in the left operand, or the value is the right operand after conversion to the type of the left operand, or a host of other possibilities. > then I think we should consider the semantics settled. I'm not trying to squash > discussion; if people don't like those semantics, let's discuss. But, I > think existing practice should weigh heavily. This semantic in gcc has been the industry standard for at least 15 years. If there have been any code written that depend upon this, you break them all. This breakage is silent, and in the type of code that you really don't want to break in this way. I object to the change.
On Jun 21, 2010, at 7:25 AM, Michael Matz wrote: > On Mon, 21 Jun 2010, Nathan Sidwell wrote: >>> Yes, namely that the author explicitely wrote a read access to vobj. >>> To demonstrate, like >>> return vobj; >>> implies a read from vobj, also >>> return vobj = x; >>> implies a read from vobj. The latter _also_ implies a store to vobj. If >>> you want the read to not happen the author better writes >>> vobj = x; >>> return x; >> >> Then what about plain >> vobj; >> ? Is that a read of vobj? > > As said, implementation-defined I think. :-( The standard waffles around with the implementation defined terminology because they don't define wires and voltages and the CPU bus. If they did, it would be plain as day what they mean. They do this only because those accesses are beyond the capability of the standard to describe well and because the standards writers are lazy and/or not skilled enough to get the language correct, The basics are pretty simple, consider a byte wide bus, single processor, no cache, with a char access. A read access of the abstract machine is mapped to read bus cycle, and a write is mapped to a write cycle. This isn't meant to be opaque or surprising. The use terminology like: What constitutes an access to an object that has volatile-qualified type is implementation-defined. because exactly what happens when you access a character on a machine that doesn't have an 8-bit wide bus is slightly trickier to define. This isn't meant to be used to arbitrarily change the mapping of the abstract machine onto the physical machine. It is wrong to think one can use the latitude in the standard due to implementation defined bits to add or erase entire loads and stores. For example, we could define all stores to volatile objects on x86_64 machines of width 64-bit to not happen, but that would be crazy, but the precise wording of the standard actually allows it. If there were a mapping standard for C on x86_64 hardware that refined and defined the abstract semantics to the actual bus, you'd discover in that standard, there would be no wiggle room for this sort of declaration. > (I think we should define it to be no access, unlike 'x = vobj;' even if x is unused later.). Again, it is wrong to abuse the latitude in the standard like this. If the standard says there is a read access on the abstract machine, then there is a read access on the physical machine (at least when the sizes match). We define an access, iff there is a access on the abstract machine. >> what about hiding the assignment inside a cond expr >> cond ? vobj = expr : 0; >> Is that a re-read of vobj? > > If that's the full expression I think we should say that this doesn't > constitute a read access. The standard describes all accesses on the abstract machine, the model we use for gcc is that every single access is mapped to a single hardware bus cycle, as long as the size of the access and the bus are the same. For a size mismatch, I'd need more room, but the rules are equally as predicable. A 64-bit store for example, on a 32-bit bus happens as two 32-bit cycles. Give that mapping, we can judge the above as wrong. I'm against the idea of using any other mapping. So, I'd rather you argue the behavior of what gcc should do, but stating what the requite semantics of the abstract machine of the language standard mandates. Then, what gcc should do is exactly match that. I object to anything else.
Mike Stump wrote: > If someone wanted to fix gcc, the proper way would be to get the C > and C++ committees on the same page, nail it down, or make it a hard > error, and remove any latitude and confusion and make them agree. Are you saying that C and C++ require different behavior for: x = y = z; where "y" is volatile? Or are you saying that C++ requires that "y" be read while C does not specify it one way or the other? Please state your position more clearly. > An assignment expression has the value of the left operand after the > assignment I don't think this is nearly as clear as you do. Furthermore, I think existing practice from other embedded compilers is pretty important; your statement that "the semantics in GCC has been the industry standard for at least 15 years" is overly strong. But, changing GCC's historic behavior is clearly not desirable, and that's an argument against eliminating the read. However, I cannot see any good justification for: int x; volatile int y; int z; x ? y = z : 0 generating a read from "y" while: x ? y = 0 : 0 does not. Which behavior are you claiming is correct here?
On 06/23/10 19:36, Mike Stump wrote: > C++ says: C and C++ differ in their cast-to-void behaviour. In particular, in C++ (void)obj; does not cause a read of obj, because no lvalue->rvalue decay occurs. This is very confusing for volatiles and incompatible with established C semantics. As such G++ will cause a read of a volatile scalar obj in these cases. For a volatile aggregate obj, it will emit a warning. In C++ the result of an assignment is an lvalue. It is not in C. > You can't just willy nilly change this: > > int i, j, k; > void foo(int&j) { > foo(i = k); > } > > can sense if you get it right or not. Add a volatile on i, and you discover, you can't just change this as you see fit, in particular, not in the way you all seem to want, which is just plain wrong. I do not know what you are saying here. Making (just) i volatile will render your code example ill-formed. > Further, C says: > > An assignment expression has the value of the left operand after the assignment > > Now, this is pretty plain, the value is the left operand. They choose this wording as they didn't want the return value to be a modifiable lvalue. I concede there is enough wiggle room here to confuse users and give implementors enough latitude to try and claim they are following the standard, but really, the return value is the valuer of the left operand, if it weren't, the standard would not say it was. It could have said, the value is the value that was stored in the left operand, or the value is the right operand after conversion to the type of the left operand, or a host of other possibilities. Are you saying that in C, both (void)obj; and (void)(obj = value); should behave the same wrt the behaviour of reads from a volatile obj? I thought there was consensus that they should not. nathan
Hi, On Wed, 23 Jun 2010, Mike Stump wrote: > > (I think we should define it to be no access, unlike 'x = vobj;' even if x is unused later.). > > Again, it is wrong to abuse the latitude in the standard like this. If > the standard says there is a read access on the abstract machine, The point of this whole thread is that the standard doesn't say there's a read access for "vobj;" (full expression). I tried to argue in this thread that there should be a read and a write access of vobj in "x = vobj = y;" . From that position some surprising consequences would follow, which I tried to either argue irrelevant or wrongly implied. I'm not sure what exactly your position on this whole matter is, except for "let's do exactly what the standard says". I'm not at all disagreeing with the position, but this very thread shows quite clearly that there's disagreement about _what_ exactly the standard says. So you should rather try to explain what you think the standard implies (I myself don't know if you're for or against a re-read). Ciao, Michael.
On Jun 23, 2010, at 10:54 PM, Mark Mitchell wrote: > Mike Stump wrote: >> If someone wanted to fix gcc, the proper way would be to get the C >> and C++ committees on the same page, nail it down, or make it a hard >> error, and remove any latitude and confusion and make them agree. > > Are you saying that C and C++ require different behavior for: > > x = y = z; > > where "y" is volatile? In my book no, they do not. I think a reasonable person can also say that they do, because the C standard can be read in a way that is difference from the C standard. C++ requires a re-read of y. For consistency, the C standard should be read in a way that requires the re-read of y. > Or are you saying that C++ requires that "y" be read while C does not specify it one way or the other? This is the essence of the second point above. > Please state your position more clearly. C++ requires a re-read of y, the patch was going to remove the re-read, I objected because the patch then makes the compiler not conform to the C++ standard. The patch was going to make C differ from C++, should the semantic for C++ be put back to match the standard. I really don't like to see the behavior of C++ and C differ when they don't need to. >> An assignment expression has the value of the left operand after the >> assignment > > I don't think this is nearly as clear as you do. Ok. Let's talk about it. I objected because people seems to be going in the wrong direction. Maybe I just misunderstood the effect of the patch. The patch removes the re-read of y, right? > Furthermore, I think existing practice from other embedded compilers is pretty important; But to the exclusion of all of gcc users and the c++ standard? > However, I cannot see any good justification for: > > int x; > volatile int y; > int z; > x ? y = z : 0 > > generating a read from "y" while: > > x ? y = 0 : 0 > > does not. I wasn't arguing this case. I believe both standards have them behaving the same.
Mike Stump wrote: >> x = y = z; >> >> where "y" is volatile? > C++ requires a re-read of y, the patch was going to remove the > re-read, I objected because the patch then makes the compiler not > conform to the C++ standard. I think that you have to read rather a lot into the C++ standard to arrive at that conclusion. But, I suggest that you raise the issue on the C++ reflector and report back. I think that it would be very helpful to hear what other implementors think. But, this point may be moot; I don't know if the proposed patch changes the behavior here or not. Nathan, can you clarify that? >> int x; volatile int y; int z; x ? y = z : 0 >> >> generating a read from "y" while: >> >> x ? y = 0 : 0 >> >> does not. > > I wasn't arguing this case. I believe both standards have them > behaving the same. And which way is that? Do you think that y needs to be read, or not?
On Jun 24, 2010, at 1:04 AM, Nathan Sidwell wrote: > On 06/23/10 19:36, Mike Stump wrote: >> C++ says: > > C and C++ differ in their cast-to-void behaviour. In particular, in C++ > (void)obj; > does not cause a read of obj, because no lvalue->rvalue decay occurs. This is very confusing for volatiles and incompatible with established C semantics. As such G++ will cause a read of a volatile scalar obj in these cases. Yeah, this was one of the later changes that I groaned over when it went in, I wish the C and C++ standards people were on the same page. If they are to differ, I would not be against just making the construct an error, to prohibit people from making any use of a semantic that isn't the same in both languages. I know the status quo is to just conform to the C semantic, and I didn't argue or object to that; it's just an unfortunate position to be in. However, since we went in that direction, I wish people would formulate a change for the C++ language consistent with that and get that in, say, in the name of C compatibility. > In C++ the result of an assignment is an lvalue. It is not in C. > >> You can't just willy nilly change this: >> >> int i, j, k; >> void foo(int&j) { >> foo(i = k); >> } >> >> can sense if you get it right or not. Add a volatile on i, and you discover, you can't just change this as you see fit, in particular, not in the way you all seem to want, which is just plain wrong. > > I do not know what you are saying here. Making (just) i volatile will render your code example ill-formed. Sorry for not expounding. I thought it would be obvious I was talking about well formed code... First case would be something like: volatile int i, j, k; volatile int vi; void foo(volatile int& j) { foo(i = k); } here, we need to bind j to the address of i. The second case is something like: volatile int i, j, k; volatile int vi; void foo(int j) { foo(i = k); } where the value passed to foo is the value read from i, and i being volatile, would mean a read of i. >> Further, C says: >> >> An assignment expression has the value of the left operand after the assignment >> >> Now, this is pretty plain, the value is the left operand. They choose this wording as they didn't want the return value to be a modifiable lvalue. I concede there is enough wiggle room here to confuse users and give implementors enough latitude to try and claim they are following the standard, but really, the return value is the valuer of the left operand, if it weren't, the standard would not say it was. It could have said, the value is the value that was stored in the left operand, or the value is the right operand after conversion to the type of the left operand, or a host of other possibilities. > > Are you saying that in C, both > > (void)obj; > and > (void)(obj = value); > > should behave the same wrt the behaviour of reads from a volatile obj? I thought there was consensus that they should not. Technically, I was arguing against the dropping of the re-read of vobj in the a = vobj = c; case. I believe doing so would cause g++ to violate the C++ language standard.
> > On 06/23/10 19:36, Mike Stump wrote: > >> C++ says: > > > > C and C++ differ in their cast-to-void behaviour. In particular, in > C++ > > (void)obj; > > does not cause a read of obj, because no lvalue->rvalue decay occurs. > This is very confusing for volatiles and incompatible with established > C semantics. As such G++ will cause a read of a volatile scalar obj in > these cases. > > Yeah, this was one of the later changes that I groaned over when it > went in, I wish the C and C++ standards people were on the same page. > If they are to differ, I would not be against just making the construct > an error, to prohibit people from making any use of a semantic that > isn't the same in both languages. I know the status quo is to just > conform to the C semantic, and I didn't argue or object to that; it's > just an unfortunate position to be in. However, since we went in that > direction, I wish people would formulate a change for the C++ language > consistent with that and get that in, say, in the name of C > compatibility. As I see it, for C++ to deviate from C in this respect is a C++ bug. Making such a construct an error in C++ programs is probably a bit strong, but I would want to see it be a warning (on by default). paul
On Jun 24, 2010, at 7:32 AM, Mark Mitchell wrote: > Mike Stump wrote: > >>> x = y = z; >>> >>> where "y" is volatile? > >> C++ requires a re-read of y, the patch was going to remove the >> re-read, I objected because the patch then makes the compiler not >> conform to the C++ standard. > > I think that you have to read rather a lot into the C++ standard to > arrive at that conclusion. I disagree. It we meant to create a temporary for a = b = c, when a b and c are all class types, we would have listed 5.17 in 12.2. Do you know of any C++ compilers that so create a temporary? g++ certainly doesn't. Now, compare 6.6.3. It can create a temporary, and it does list 12.2, and is listed by 12.2. Do you think: (0, a) can create a temporary for a as well? The standard uses similar wording there as well: The type and value of the result are the type and value of the right operand; the result is an lvalue if its right operand is. I think what your missing is kinda basic, from 1.7: An object is created by a definition (_basic.def_), by a new-expression (_expr.new_) or by the imple- mentation (_class.temporary_) when needed. This is meant to describe when objects are created. We don't have a definition, so basic.def doesn't apply. We don't have a new, so expr.new doesn't apply, and class.temporary doesn't apply, so, there is in fact no object created. If one had been created the standard would has said it. I think your confusing the as-if rule, which says, you can create temporaries all you want, as long as you can't observe it. This is different, we can observe it, so it can't be created. If you want to claim an object is create, start from 1.7, and tell me which of basic.def, expr.new or class.temporary is used, and we'll work forward from there. Now, how do I know we're dealing with an object, because of 3.10: An lvalue refers to an object or function. Now, how do I know we're dealing with an lvalue: the result is an lvalue QED. So, the standard is perfectly clear on this point, but I do admit, the wording could be improved in 5.17. > But, I suggest that you raise the issue on the C++ reflector and report back. I think that it would be very helpful to hear what other implementors think. I'd rather just survey what their compilers do. If they all agree with you, maybe indeed g++ is the odd man out. I have a sneaky suspicion that none of them do. When all compilers match, I don't usually waste the reflector's time on such trivial matters, though, would be good to reword the standard to say the result is the left hand side, so that people are less able to be confused by the wording. > But, this point may be moot; I don't know if the proposed patch changes > the behavior here or not. Nathan, can you clarify that? I thought his patch was clear: + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_8" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_8," } } */ + return cond ? obj_8 = 0 : 0; >>> int x; volatile int y; int z; x ? y = z : 0 >>> >>> generating a read from "y" while: >>> >>> x ? y = 0 : 0 >>> >>> does not. >> >> I wasn't arguing this case. I believe both standards have them >> behaving the same. > > And which way is that? Do you think that y needs to be read, or not? In: volatile int i, j, k; volatile int vi; void foo(int p) { k = (i ? j=0 : k); } j is re-read. It is re-read regardless if j=0, or j=k is used in the source. Now, for the simpler: volatile int i, j, k; volatile int vi; void foo(int p) { i ? j=0 : 0; } neither gcc nor g++ re-reads, and I'm not arguing changing that. Nor is anyone suggesting changing that, so I don't see the point of asking. I'm dodging what the standard says, well, unless someone wants to propose we change how they behave currently.
On 06/24/10 15:32, Mark Mitchell wrote: > Mike Stump wrote: > >>> x = y = z; >>> >>> where "y" is volatile? > >> C++ requires a re-read of y, the patch was going to remove the >> re-read, I objected because the patch then makes the compiler not >> conform to the C++ standard. > > I think that you have to read rather a lot into the C++ standard to > arrive at that conclusion. But, I suggest that you raise the issue on > the C++ reflector and report back. I think that it would be very > helpful to hear what other implementors think. > > But, this point may be moot; I don't know if the proposed patch changes > the behavior here or not. Nathan, can you clarify that? Yes, it changes the behaviour for C++ in the same way it changes it for C. Specifically the assigned-to object is not reread. For C and C++ to have different behaviour wrt volatile accesses is a mistake (hence, as I mentioned, previous changes to G++ to make it have the same behaviour as gcc). nathan
On 06/24/10 16:42, Mike Stump wrote: > Sorry for not expounding. I thought it would be obvious I was talking about well formed code... First case would be something like: > > volatile int i, j, k; > volatile int vi; > void foo(volatile int& j) { > foo(i = k); > } > > here, we need to bind j to the address of i. The second case is something like: Correct. The patch does not change this behaviour -- the C++ front end already has to explicitly break out the lvalue from the assignment to conform to the semantics of TREEs. The current G++ behaviour is not to re-read the assigned-to value. > volatile int i, j, k; > volatile int vi; > void foo(int j) { > foo(i = k); > } > > where the value passed to foo is the value read from i, and i being volatile, would mean a read of i. This is just the use-of-assignment value embedded in a different context. Or do you consider it somewhat different from the non_vol = vol = expr; case? >>> Further, C says: >>> >>> An assignment expression has the value of the left operand after the assignment >>> >>> Now, this is pretty plain, the value is the left operand. They choose this wording as they didn't want the return value to be a modifiable lvalue. I concede there is enough wiggle room here to confuse users and give implementors enough latitude to try and claim they are following the standard, but really, the return value is the valuer of the left operand, if it weren't, the standard would not say it was. It could have said, the value is the value that was stored in the left operand, or the value is the right operand after conversion to the type of the left operand, or a host of other possibilities. >> >> Are you saying that in C, both >> >> (void)obj; >> and >> (void)(obj = value); >> >> should behave the same wrt the behaviour of reads from a volatile obj? I thought there was consensus that they should not. > > Technically, I was arguing against the dropping of the re-read of vobj in the a = vobj = c; case. I believe doing so would cause g++ to violate the C++ language standard You stated something about the C standard, my question was about the behaviour in C to clarify what you meant. Do you have an opinion about that? I am having a hard time following your reasoning, as it appears to switch between C and C++ standards. nathan
On 06/24/10 19:38, Mike Stump wrote: > On Jun 24, 2010, at 7:32 AM, Mark Mitchell wrote: >> Mike Stump wrote: >> >>>> x = y = z; >>>> >>>> where "y" is volatile? >> >>> C++ requires a re-read of y, the patch was going to remove the >>> re-read, I objected because the patch then makes the compiler not >>> conform to the C++ standard. >> >> I think that you have to read rather a lot into the C++ standard to >> arrive at that conclusion. > > I disagree. It we meant to create a temporary for a = b = c, when a b and c are all class types, we would have listed 5.17 in 12.2. Do you know of any C++ compilers that so create a temporary? g++ certainly doesn't. Now, compare 6.6.3. It can create a temporary, and it does list 12.2, and is listed by 12.2. We were talking about scalars. volatile structs are treated differently. As I already mentioned, in G++, '(void)volatile_class_object' does not cause lvalue->rvalue decay and does not cause a read of all the members of the object. I think the behaviour of volatile structs in C++ is too ill-specified to meaningfully talk about when accesses to the members occur. I think the goal should be to make accesses to volatile scalars, as used when poking at hardware, follow simple, composable, consistent rules. IMHO the current implemented behaviour is none of those. > j is re-read. It is re-read regardless if j=0, or j=k is used in the source. Now, for the simpler: > > volatile int i, j, k; > volatile int vi; > void foo(int p) { > i ? j=0 : 0; > } > > neither gcc nor g++ re-reads, and I'm not arguing changing that. Nor is anyone suggesting changing that, so I don't see the point of asking. I'm dodging what the standard says, well, unless someone wants to propose we change how they behave currently. But gcc does reread when assigning a non-constant value. As I pointed out, GCC's behaviour is inconsistent. nathan.
On Jun 24, 2010, at 11:35 PM, Nathan Sidwell wrote: > On 06/24/10 16:42, Mike Stump wrote: > >> Sorry for not expounding. I thought it would be obvious I was talking about well formed code... First case would be something like: >> >> volatile int i, j, k; >> volatile int vi; >> void foo(volatile int& j) { >> foo(i = k); >> } >> >> here, we need to bind j to the address of i. The second case is something like: > > Correct. The patch does not change this behaviour -- the C++ front end already has to explicitly break out the lvalue from the assignment to conform to the semantics of TREEs. The current G++ behaviour is not to re-read the assigned-to value. g++ seems not to share your position: movl _i, %eax movl %eax, (%esp) volatile int i, j, k; volatile int vi; void foo(int j) { foo(i = k); } GNU C++ (GCC) version 4.6.0 20100620 (experimental) [trunk revision 161045] (x86_64-apple-darwin10) compiled by GNU C version 4.2.1 (Apple Inc. build 5659), GMP version 5.0.1 ? Certainly I must misunderstand something, can you spot it? > This is just the use-of-assignment value embedded in a different context. Or do you consider it somewhat different from the > non_vol = vol = expr; > case? I don't believe that standard distinguishes. > >>>> Further, C says: >>>> >>>> An assignment expression has the value of the left operand after the assignment >>>> >>>> Now, this is pretty plain, the value is the left operand. They choose this wording as they didn't want the return value to be a modifiable lvalue. I concede there is enough wiggle room here to confuse users and give implementors enough latitude to try and claim they are following the standard, but really, the return value is the valuer of the left operand, if it weren't, the standard would not say it was. It could have said, the value is the value that was stored in the left operand, or the value is the right operand after conversion to the type of the left operand, or a host of other possibilities. >>> >>> Are you saying that in C, both >>> >>> (void)obj; >>> and >>> (void)(obj = value); >>> >>> should behave the same wrt the behaviour of reads from a volatile obj? I thought there was consensus that they should not. >> >> Technically, I was arguing against the dropping of the re-read of vobj in the a = vobj = c; case. I believe doing so would cause g++ to violate the C++ > language standard > > You stated something about the C standard, my question was about the behaviour in C to clarify what you meant. Do you have an opinion about that? The standard has an opinion I respect: An assignment expression has the value of the left operand value in this context means: 3.15 [#1] object region of data storage in the execution environment, the contents of which can represent values | and the left operand means vobj (in your snippet). C is the same. C++ interpreted what the C standard meant at time and tried to clarify. There is a slight mod in this area due to C++, which is why the language is slightly different, but the intent of the C++ committee was to make them match C. The standards have many tricky areas, many hard to read things, but when the chain of reasoning spans just 2 sections, and uses 1 or two verbs each, using simple, well defined terms, I just don't see the room to be confused. > I am having a hard time following your reasoning, as it appears to switch between C and C++ standards. I don't switch between them, I've expounded on both, because others brought both up. In this case, one can be used to understand the meaning of the other, as both are intended to match (save for the very obvious lvalue result), that was the one addition the C++ people did to better match the requirements for C++. C has no need to adopt that, but since the pulled in modifiable lvalue, they could pull in this change safely, if they wanted. Previously, they didn't have the terminology to do it, now they do.
On Jun 24, 2010, at 11:50 PM, Nathan Sidwell wrote: > On 06/24/10 19:38, Mike Stump wrote: >> On Jun 24, 2010, at 7:32 AM, Mark Mitchell wrote: >>> Mike Stump wrote: >>> >>>>> x = y = z; >>>>> >>>>> where "y" is volatile? >>> >>>> C++ requires a re-read of y, the patch was going to remove the >>>> re-read, I objected because the patch then makes the compiler not >>>> conform to the C++ standard. >>> >>> I think that you have to read rather a lot into the C++ standard to >>> arrive at that conclusion. >> >> I disagree. It we meant to create a temporary for a = b = c, when a b and c are all class types, we would have listed 5.17 in 12.2. Do you know of any C++ compilers that so create a temporary? g++ certainly doesn't. Now, compare 6.6.3. It can create a temporary, and it does list 12.2, and is listed by 12.2. > > We were talking about scalars. I don't find any limitation in 5.17 to scalars or to objects of class type. If you examine the wording in 8.5.3 for example, a place that we all know does create temporaries: --Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non-refer- ence copy initialization (_dcl.init_). The reference is then bound to the temporary. we can see the usual language the standard uses to create temporaries. It _must_ do this, so that the results of == on &lval can be defined. We use 12.2 as a proxy to find places in the standard that create temporaries. One can also just search the standard for the word temporary, if one wanted to. Since 5.17 doesn't distinguish between class nor scalars, we are free to use the semantics of class types to better understand the semantics for scalars. These semantics _must_ be the same, because there is no stated difference. The standard is not meant to be misread. It isn't formal enough to withstand people intentionally or otherwise, misreading it. So, what we are talking about are the semantics of 5.17, and those are not limited to scalars. > volatile structs are treated differently. In a = vob = c; stucts are treated the same. This is the case we were discussing. We can know that, because it appears on the 6th lines of this email. > I think the behaviour of volatile structs in C++ is too ill-specified to meaningfully talk about when accesses to the members occur. Again, I disagree. We could embark upon what the standard says, but, let's just agree to not, unless someone wants to learn what it says, or someone wants to change the status quo behavior in a way that deviates from the standard. > I think the goal should be to make accesses to volatile scalars, as used when poking at hardware, follow simple, composable, consistent rules. Yeah, the problem with that is implicit in that, is, I want to make the compiler agree to the ruleset I just invented because I like it, and if we do that, the semantics then change day by day, by the whim of the person doing the patch today, and the each implementor gets to choose a different rule, and the users don't benefit. The other possibility is to make it conform to the language standard, when reasonable. I think the later is the best option. No one has argued that they understand what the standard says and want to intentionally deviate from it, nor have they stated a good reason for doing that. So, could you please restate what you want to do. Do you want gcc to follow the language standard, or, would you rather ignore the standard and just invent some rules on the fly for gcc to follow? > IMHO the current implemented behaviour is none of those. Concerning: Sometimes we re-read the assigned-to object, and sometimes we do not. For instance, return vobj = data; will cause a reread of vobj, IF data is not a constant. the proper solution is to make it always reread vobj. Simple and consistent, while matching the standard, no? This sort of bug happens because people forget the, this optimization isn't valid when TREE_THIS_VOLATILE is set. The solution is to add the missing check. We know it is the optimizer, as if you replace the constant with anything but a constant, we get the proper semantics. That's proof positive that the bug lies no in the semantics of all other cases, but rather the semantics of the constant case. >> j is re-read. It is re-read regardless if j=0, or j=k is used in the source. Now, for the simpler: >> >> volatile int i, j, k; >> volatile int vi; >> void foo(int p) { >> i ? j=0 : 0; >> } >> >> neither gcc nor g++ re-reads, and I'm not arguing changing that. Nor is anyone suggesting changing that, so I don't see the point of asking. I'm dodging what the standard says, well, unless someone wants to propose we change how they behave currently. > > But gcc does reread when assigning a non-constant value. As I pointed out, GCC's behaviour is inconsistent. Ah, I want hoping to avoid that case... Oh well... [ pause ] g++ doesn't reread in that case. :-) I think that matches the language standard 5.16: 4 If the second and third operands are lvalues and have the same type, the result is of that type and is an lvalue. and no decay, no access. I don't see that as problematic. gcc does access, and I think that follows the C standard. When exploring it, we find only constants screw it up, so the bug again, is in the optimizer, and the code missing is the one that says, don't do this optimization when volatile. When that bug is fixed, gcc (c language) is then consistent with itself. As for the inconsistency between C and C++. I don't feel as strongly on how to resolve this. I might vote to leave the g++ hack out here, but if people wanted the hack to be all lvalues cast to void are converted to rvalues, I don't feel strongly enough to oppose, though, I wish people would rather hit up the C and C++ people, lock them in a room, and say you can't come out until one of you relents. I'd also be ok with it being a hard error, as this can be refined later when the standards people agree on a consistent semantic. Hard error (off with pedantic) or a note might be my preferred solution.
Mike Stump wrote: > So, could you please restate what you want to do. Do you want gcc to > follow the language standard, or, would you rather ignore the > standard and just invent some rules on the fly for gcc to follow? There's no need to take that tone. Nathan and I have both participated in standards committees and in making G++ much more conformant to the standard. Of course we don't want to make things up on the fly. But, you are asserting that the standard unambiguously mandates a particular behavior. I, at least, don't think that's clear. I also don't think that in this area the standard is more important than existing practice, both in GCC and in other compilers. Rather than all this discussion of the standard, I'd like to see a list of the cases that are controversial, together with a table showing what compilers do reads and which do not. Nathan, perhaps you can put together the table? And then we can ask volunteers to start filling it in? Thanks,
On Jun 25, 2010, at 11:44 AM, Mark Mitchell wrote: > There's no need to take that tone. Sorry for the tone. I honestly don't know if people want to deviate from the standard, and if so, why, or if they just have a different idea of what it says, or why they think it says what they think. > Of course we don't want to make things up on the fly. Very little of the preceding has any backing from the language of the standard for the proposed behavior change, hence my confusion. > But, you are asserting that the standard unambiguously mandates a particular behavior. Yes, > I, at least, don't think that's clear. I'd like to understand how you interpret the standard. If you could point out the support in the standard for a copy to be made, that would help me understand better. Other compilers might differ on the re-read. Even if they did, I'd still object to changing gcc to match them. I'd rather talk with their implementors and figure out if they think they conform to the standard or not, and if not, why? Could be just a simple bug in their compiler that they would fix.
Mike Stump wrote: > I'd like to understand how you interpret the standard. I simply think it does not say. I don't think it says make a copy. I don't think it says don't make a copy. I understand how you are reading it, but I think you're finding meaning where none is necessarily present. I'll be persuaded if you show me a consensus of people on the standards reflector, but you didn't want to ask on the reflector. If it is unspecified, I'd rather not do the read. I don't argue the standard mandates not doing the read, but I feel that it's better behavior. I think it's odd to do a read from "y" in "x = y = z", but I think it's *very* odd to do a read from "y" in the case of "x ? y = z : ...", which is what Nathan says we presently do. > Other compilers might differ on the re-read. Even if they did, I'd > still object to changing gcc to match them. I'd rather talk with > their implementors and figure out if they think they conform to the > standard or not, and if not, why? Could be just a simple bug in > their compiler that they would fix. I don't find that likely. Changing this behavior will change the meaning of existing programs in mysterious ways. I doubt you'll find compiler implementors eager to do that. But, we can't know until we test the compilers. Maybe they all agree with you. Backwards compatibility is also the best reason not to change GCC; even if the standard unambiguously mandated that we change, it might not make sense to change, since it might break code that's worked with GCC for a long time. But, that doesn't resolve the constant vs. non-constant question. That's an inconsistency so weird and horrid that I think we have to change either the constant case or the non-constant case. If we don't make those cases consistent, we'll find that optimization levels affect whether a volatile location is read and that's very bad indeed. So, I think that GCC is going to have to change it's behavior in one or the other of these cases. Right now you're asserting that the standard mandates a particular behavior, but you seem unwilling to back that up by getting a consensus on the reflector, and you're saying that existing practice by other compilers won't change your opinion. That seems a pretty rigid position. I'd be a lot more persuaded if you helped to establish either a consensus on the standards reflector or demonstrated a consensus of other compilers.
> I honestly don't know if people want to deviate from the standard, and if > so, why, or if they just have a different idea of what it says, or why > they think it says what they think. Implementing a standard precisely is good. Doing what users expect is also good. When those two conflict, we have to choose which to do. The former is USUALLY the right choice, but not always. It depends on the level of ambiguity in the standard and the historical legimacy of the expectation.
On Sat, Jun 26, 2010 at 5:46 AM, Mark Mitchell <mark@codesourcery.com> wrote: > Mike Stump wrote: > >> I'd like to understand how you interpret the standard. > > I simply think it does not say. I don't think it says make a copy. I > don't think it says don't make a copy. I understand how you are reading > it, but I think you're finding meaning where none is necessarily > present. I'll be persuaded if you show me a consensus of people on the > standards reflector, but you didn't want to ask on the reflector. > > If it is unspecified, I'd rather not do the read. I don't argue the > standard mandates not doing the read, but I feel that it's better > behavior. I think it's odd to do a read from "y" in "x = y = z", but I > think it's *very* odd to do a read from "y" in the case of "x ? y = z : > ...", which is what Nathan says we presently do. > >> Other compilers might differ on the re-read. Even if they did, I'd >> still object to changing gcc to match them. I'd rather talk with >> their implementors and figure out if they think they conform to the >> standard or not, and if not, why? Could be just a simple bug in >> their compiler that they would fix. > > I don't find that likely. Changing this behavior will change the > meaning of existing programs in mysterious ways. I doubt you'll find > compiler implementors eager to do that. But, we can't know until we > test the compilers. Maybe they all agree with you. > > Backwards compatibility is also the best reason not to change GCC; even > if the standard unambiguously mandated that we change, it might not make > sense to change, since it might break code that's worked with GCC for a > long time. What did GCC do for all these cases pre-tree-ssa? Richard.
Hi, On Sat, 26 Jun 2010, Richard Guenther wrote: > > Backwards compatibility is also the best reason not to change GCC; > > even if the standard unambiguously mandated that we change, it might > > not make sense to change, since it might break code that's worked with > > GCC for a long time. > > What did GCC do for all these cases pre-tree-ssa? Trying 3.3, and this testcase: ----------------------- volatile int vobj1, vobj2, vobj3, vobj4, vobj5, vobj6, vobj7, vobj8, vobj9; int x, y, z; void f1 (int i) { vobj1 = y; x = vobj2 = y; x ? vobj3 = y : z; x = y ? vobj4 = z : i; vobj5; vobj6 = 42; x = vobj7 = 43; x ? vobj8 = 44 : z; x = y ? vobj9 = 45 : i; } ----------------------- I.e. testing all cases we were discussing in this thread, constant and non-constant RHS. cc1 and cc1plus generate the same accesses, in addition optimization levels make no difference (as expected). The relevant instructions are this in all cases: % grep 'mov.*vobj' volatile-check.s movl %edx, vobj1 movl %edx, vobj2 movl vobj2, %eax movl %edx, vobj3 movl %eax, vobj4 movl vobj4, %eax movl vobj5, %eax movl %eax, vobj6 movl %eax, vobj7 movl vobj7, %eax movl %ecx, vobj8 movl %eax, vobj9 I.e. a (re-)read occurs for vobj2, vobj4, vobj5 and vobj7, that is these cases: x = vobj2 = y; x = y ? vobj4 = z : i; vobj5; x = vobj7 = 43; Interestingly a re-read doesn't occur for vobj9: x = y ? vobj9 = 45 : i; that is the same case as vobj4, just with a constant RHS. I'd consider this a bug. (For completeness, the reread of vobj4 of course only happens conditionally) FWIW, our current trunk in addition generates re-reads for vobj3, vobj8 and vobj9. If one were to follow my (and Mikes?) reading of the standard rereading vobj3 and vobj8 would be wrong. All of these compilers agree that a read is to be emitted for a bare vobj access in void context: "vobj5;" but not for the simple assignment "vobj1 = x;". ICC 11.0 and 11.1 only generate a read of vobj5 (i.e. no re-reading occurs at all) for the C language, and no read at all for the C++ language. Ciao, Michael.
Michael Matz wrote: > Trying 3.3, and this testcase: Thanks for doing this research! Would you be willing to make a Wiki page with this information on it? I think if we can fill in a table showing which compilers do what, that would be extremely helpful. If we had each of the examples you collected in rows, and columns showing whether each compiler reads the volatile variable, we could see just how much consensus there is. > ICC 11.0 and 11.1 only generate a read of vobj5 (i.e. no re-reading occurs > at all) for the C language, and no read at all for the C++ language. That's interesting. Of course, ICC is not terribly heavily used as an embedded compiler, which is where there is the most use of volatile. (Of course, volatile is used in multi-threaded non-embedded programs as well, though there the exact number of reads/writes is probably less important to many users.) Thanks,
Hi,
On Sat, 26 Jun 2010, Mark Mitchell wrote:
> Would you be willing to make a Wiki page with this information on it?
http://gcc.gnu.org/wiki/VolatileAccessComparison
Ciao,
Michael.
Michael Matz wrote: >> Would you be willing to make a Wiki page with this information on it? > > http://gcc.gnu.org/wiki/VolatileAccessComparison Thanks! This is a big help. I filled in rows for RealView 3.1 and RealView 4.0 and for Visual C++ 2010. It looks like RealView behaves like ICC, which isn't too surprising given that they are both EDG-based compilers. On the other Visual C++ behaves like the current 4.6 trunk. So, we certainly do not have agreement across implementors as to what behavior is right. I'd like to see what some of the other embedded compilers like Green Hills, CodeWarrior, IAR, and Keil do. One thing that I'm confused about is that Nathan reported inconsistency between the constant and non-constant cases. But, while your table shows that for GCC 3.3, it doesn't show it for the current 4.6 trunk. Nathan, are we missing something in the test cases relative to the inconsistency you reported? Thanks,
On 06/25/10 17:41, Mike Stump wrote: > On Jun 24, 2010, at 11:35 PM, Nathan Sidwell wrote: >> On 06/24/10 16:42, Mike Stump wrote: >> >>> Sorry for not expounding. I thought it would be obvious I was talking about well formed code... First case would be something like: >>> >>> volatile int i, j, k; >>> volatile int vi; >>> void foo(volatile int& j) { >>> foo(i = k); >>> } >>> >>> here, we need to bind j to the address of i. The second case is something like: >> >> Correct. The patch does not change this behaviour -- the C++ front end already has to explicitly break out the lvalue from the assignment to conform to the semantics of TREEs. The current G++ behaviour is not to re-read the assigned-to value. > > g++ seems not to share your position: > > movl _i, %eax > movl %eax, (%esp) > > volatile int i, j, k; > volatile int vi; > void foo(int j) { > foo(i = k); > } > > GNU C++ (GCC) version 4.6.0 20100620 (experimental) [trunk revision 161045] (x86_64-apple-darwin10) > compiled by GNU C version 4.2.1 (Apple Inc. build 5659), GMP version 5.0.1 > > ? Certainly I must misunderstand something, can you spot it? You have changed the testcase to be passing by value from passing by reference. Why do you think those would be equivalent? nathan
On 06/26/10 20:57, Mark Mitchell wrote: > One thing that I'm confused about is that Nathan reported inconsistency > between the constant and non-constant cases. But, while your table > shows that for GCC 3.3, it doesn't show it for the current 4.6 trunk. > Nathan, are we missing something in the test cases relative to the > inconsistency you reported? hm, I have just tested mainling, and I see the same as Michael -- namely a reread for constant assignments. GCC 4.4.3 is different though -- it does not do the reread. I'll add another row to the wiki, with 4.4.3's behaviour. nathan
On 06/28/10 08:06, Nathan Sidwell wrote: > On 06/26/10 20:57, Mark Mitchell wrote: > >> One thing that I'm confused about is that Nathan reported inconsistency >> between the constant and non-constant cases. But, while your table >> shows that for GCC 3.3, it doesn't show it for the current 4.6 trunk. >> Nathan, are we missing something in the test cases relative to the >> inconsistency you reported? > > hm, I have just tested mainling, and I see the same as Michael -- namely > a reread for constant assignments. GCC 4.4.3 is different though -- it > does not do the reread. sigh, I'd got .i and .ii suffixes mixed up. it appears GCC 4.4.3 has different behaviour for C and C++. I'll be really careful filling in the table. nathan
On 06/25/10 19:14, Mike Stump wrote: > I don't find any limitation in 5.17 to scalars or to objects of class type. If you examine the wording in 8.5.3 for example, a place that we all know does create temporaries: I did not make the claim that the standard distinguished. I am make the claim that we want simple, consistent and composable rules for accessing scalar device registers. As you brought it up, I stated what I thought of volatile structure semantics. > In a = vob = c; stucts are treated the same. This is the case we were discussing. We can know that, because it appears on the 6th lines of this email. The case of structs is what you have brought to the mix. The original discussion was about scalars. >> I think the behaviour of volatile structs in C++ is too ill-specified to meaningfully talk about when accesses to the members occur. > > Again, I disagree. We could embark upon what the standard says, but, let's just agree to not, unless someone wants to learn what it says, or someone wants to change the status quo behavior in a way that deviates from the standard. If you do not want to discuss what the standard says, why did you bring it up? This whole discussion is about discrepancies within the standards and between them and desired semantics. >> I think the goal should be to make accesses to volatile scalars, as used when poking at hardware, follow simple, composable, consistent rules. > > Yeah, the problem with that is implicit in that, is, I want to make the compiler agree to the ruleset I just invented because I like it, and if we do that, the semantics then change day by day, by the whim of the person doing the patch today, and the each implementor gets to choose a different rule, and the users don't benefit. The other possibility is to make it conform to the language standard, when reasonable. I think the later is the best option. No one has argued that they understand what the standard says and want to intentionally deviate from it, nor have they stated a good reason for doing that. Could you rephrase that please, I cannot understand what you are claiming. > So, could you please restate what you want to do. Do you want gcc to follow the language standard, or, would you rather ignore the standard and just invent some rules on the fly for gcc to follow? I claim the C and C++ standards are both not clear and at odds semantics that are necessary for hardware programming. >> IMHO the current implemented behaviour is none of those. > > Concerning: > > Sometimes we re-read the assigned-to object, and sometimes we do not. For instance, > return vobj = data; > will cause a reread of vobj, IF data is not a constant. > > the proper solution is to make it always reread vobj. Simple and consistent, while matching the standard, no? did you not read my original problem statement? You seem to be ignoring associated issues that come with that interpretation, which I mentioned. Could you clarify what you also claim the following expression-statements should reread? vobj; vobj = data; expr, vobj = data; They all have values. nathan
On Jun 28, 2010, at 12:44 AM, Nathan Sidwell wrote: > did you not read my original problem statement? I did read it. > You seem to be ignoring associated issues that come with that interpretation, which I mentioned. I'm aware of them. We'd all benefit if the C and C++ committees refined the language to make things more clear and to match each other. > Could you clarify what you also claim the following expression-statements should reread? Below is my take on what we should do. > vobj; read. > vobj = data; no re-read > expr, vobj = data; no re-read.
On Jun 27, 2010, at 11:28 PM, Nathan Sidwell wrote: > On 06/25/10 17:41, Mike Stump wrote: >> On Jun 24, 2010, at 11:35 PM, Nathan Sidwell wrote: >>> On 06/24/10 16:42, Mike Stump wrote: >>> >>>> Sorry for not expounding. I thought it would be obvious I was talking about well formed code... First case would be something like: >>>> >>>> volatile int i, j, k; >>>> volatile int vi; >>>> void foo(volatile int& j) { >>>> foo(i = k); >>>> } >>>> >>>> here, we need to bind j to the address of i. The second case is something like: >>> >>> Correct. The patch does not change this behaviour -- the C++ front end already has to explicitly break out the lvalue from the assignment to conform to the semantics of TREEs. The current G++ behaviour is not to re-read the assigned-to value. >> >> g++ seems not to share your position: >> >> movl _i, %eax >> movl %eax, (%esp) >> >> volatile int i, j, k; >> volatile int vi; >> void foo(int j) { >> foo(i = k); >> } >> >> GNU C++ (GCC) version 4.6.0 20100620 (experimental) [trunk revision 161045] (x86_64-apple-darwin10) >> compiled by GNU C version 4.2.1 (Apple Inc. build 5659), GMP version 5.0.1 >> >> ? Certainly I must misunderstand something, can you spot it? > > You have changed the testcase to be passing by value from passing by reference. Why do you think those would be equivalent? The read happens when the the object referred to by the reference is accessed: volatile int i, j; int x, data, expr, k; static inline void foo(volatile int &r) { int fetch = r; } int main() { foo(i=k); } movl _k, %eax movl %eax, _i movl _i, %eax here, i is re-read. Here, g++ in fact does re-read the assigned to value.
On 06/30/10 21:56, Mike Stump wrote: > Below is my take on what we should do. > >> vobj; > > read. > >> vobj = data; > > no re-read > >> expr, vobj = data; > > no re-read. Ok, I think everyone's agreed about that. Do you have a reference to the C standard that clarifies the difference? Wrt the std, all those expression-statements have values - the first is an lvalue, the other 2 are rvalues. Another example I missed is: expr, vobj; I think that should read vobj. But AFAICT 'expr, vobj = data;' and 'expr, vobj;' are identically rvalued according to the C std. Is there anything in the std that justifies them behaving differently? nathan
On 07/01/10 02:02, Mike Stump wrote: > On Jun 27, 2010, at 11:28 PM, Nathan Sidwell wrote: >> On 06/25/10 17:41, Mike Stump wrote: >>> On Jun 24, 2010, at 11:35 PM, Nathan Sidwell wrote: >>>> On 06/24/10 16:42, Mike Stump wrote: >>>> >>>>> Sorry for not expounding. I thought it would be obvious I was talking about well formed code... First case would be something like: >>>>> >>>>> volatile int i, j, k; >>>>> volatile int vi; >>>>> void foo(volatile int& j) { >>>>> foo(i = k); >>>>> } >>>>> >>>>> here, we need to bind j to the address of i. The second case is something like: >>>> >>>> Correct. The patch does not change this behaviour -- the C++ front end already has to explicitly break out the lvalue from the assignment to conform to the semantics of TREEs. The current G++ behaviour is not to re-read the assigned-to value. >>> >>> g++ seems not to share your position: >>> >>> movl _i, %eax >>> movl %eax, (%esp) >>> >>> volatile int i, j, k; >>> volatile int vi; >>> void foo(int j) { >>> foo(i = k); >>> } >>> >>> GNU C++ (GCC) version 4.6.0 20100620 (experimental) [trunk revision 161045] (x86_64-apple-darwin10) >>> compiled by GNU C version 4.2.1 (Apple Inc. build 5659), GMP version 5.0.1 >>> >>> ? Certainly I must misunderstand something, can you spot it? >> >> You have changed the testcase to be passing by value from passing by reference. Why do you think those would be equivalent? > > The read happens when the the object referred to by the reference is accessed: You're now changing the example again. This time with a reference to volatile object. > > volatile int i, j; > int x, data, expr, k; > > static inline void foo(volatile int&r) { > int fetch = r; > } > > int main() { > foo(i=k); > } > > movl _k, %eax > movl %eax, _i > movl _i, %eax > > here, i is re-read. Here, g++ in fact does re-read the assigned to value. Yes I would expect this -- there is a sequence point between the assignment to i in the argument list, and the read of r in the inlined function. I do not understand the point you are trying to make. nathan
On Jul 5, 2010, at 2:02 AM, Nathan Sidwell wrote: > On 07/01/10 02:02, Mike Stump wrote: >> On Jun 27, 2010, at 11:28 PM, Nathan Sidwell wrote: >>> On 06/25/10 17:41, Mike Stump wrote: >>>> On Jun 24, 2010, at 11:35 PM, Nathan Sidwell wrote: >>>>> The current G++ behaviour is not to re-read the assigned-to value. > >> Here, g++ in fact does re-read the assigned to value. > > Yes I would expect this Above you say that it doesn't re-read, then you agree with me that it does re-read... :-) Anyway, we're on the same page now with respect to this part of the subthread. I suppose that if we defined re-read to be the process by which the value when used is re-fetched from memory instead of a cached value of the rhs then with that definition gcc always (barring stupid bugs under discussion) re-reads and never doesn't (or at least, if one agreed with me that the result is the value of the lhs). For those that go with, the value is the converted value of the rhs, then we'd say that gcc never re-reads.
On Jul 5, 2010, at 1:59 AM, Nathan Sidwell wrote: > Do you have a reference to the C standard that clarifies the difference? Sure, it would be the part where the edits are when we ask them to regularize the rules with C++ after C++ fixes what they broke. :-) That is why I carefully chose the phrase `what we should do' as opposed to, what the standard says... > Wrt the std, all those expression-statements have values - the first is an lvalue, the other 2 are rvalues. Yeah, I know. > Another example I missed is: > expr, vobj; > > I think that should read vobj. :-) gcc fetches. I'd not oppose that. > But AFAICT 'expr, vobj = data;' and 'expr, vobj;' are identically rvalued according to the C std. Is there anything in the std that justifies them behaving differently? Nope, don't think so, at least in the C standard.
On 07/09/10 06:14, Mike Stump wrote: > On Jul 5, 2010, at 2:02 AM, Nathan Sidwell wrote: >> On 07/01/10 02:02, Mike Stump wrote: >>> On Jun 27, 2010, at 11:28 PM, Nathan Sidwell wrote: >>>> On 06/25/10 17:41, Mike Stump wrote: >>>>> On Jun 24, 2010, at 11:35 PM, Nathan Sidwell wrote: >>>>>> The current G++ behaviour is not to re-read the assigned-to value. >> >>> Here, g++ in fact does re-read the assigned to value. >> >> Yes I would expect this > > > Above you say that it doesn't re-read, then you agree with me that it does re-read... :-) As I said, the two examples are different. Your modified example inserts a sequence point (but you've snipped my sentence explaining that). > Anyway, we're on the same page now with respect to this part of the subthread. I suppose that if we defined re-read to be the process by which the value when used is re-fetched from memory instead of a cached value of the rhs then with that definition gcc always (barring stupid bugs under discussion) re-reads and never doesn't (or at least, if one agreed with me that the result is the value of the lhs). For those that go with, the value is the converted value of the rhs, then we'd say that gcc never re-reads. No, we are not. nathan
On 07/09/10 06:27, Mike Stump wrote: > On Jul 5, 2010, at 1:59 AM, Nathan Sidwell wrote: >> Do you have a reference to the C standard that clarifies the difference? > > Sure, it would be the part where the edits are when we ask them to regularize the rules with C++ after C++ fixes what they broke. :-) That is why I carefully chose the phrase `what we should do' as opposed to, what the standard says... > >> Wrt the std, all those expression-statements have values - the first is an lvalue, the other 2 are rvalues. > > Yeah, I know. > >> Another example I missed is: >> expr, vobj; >> >> I think that should read vobj. > > :-) gcc fetches. I'd not oppose that. > >> But AFAICT 'expr, vobj = data;' and 'expr, vobj;' are identically rvalued according to the C std. Is there anything in the std that justifies them behaving differently? > > Nope, don't think so, at least in the C standard. Good, so I think we're all agreed that what the C standard says is not the behaviour we want. nathan
This discussion appears to have gone quiet. From the table in http://gcc.gnu.org/wiki/VolatileAccessComparison I think it clear that: * GCC's current behaviour is inconsistent * Historically GCC's behaviour has changed. GCC 4.3's behaviour is particularly unfortunate WRT C and C++ consistency. * The 2 other compilers tested (I'm lumping all the EDG-based ones together) implement different access patterns. One of which appears to be the same as the current GCC development semantics. * I missed another disambiguating testcase, namely '0, vobj = x;' in C the result of the comma operator is not an lvalue -- its result has the same properties as the result of the conditional operator. That argues for them behaving the same -- which currently does not happen in GCC. I think there is also consensus that what the standard literally says are not useful semantics. Is there consensus on what the semantics should be? Any suggestions as to how to move forward? nathan
Nathan Sidwell wrote: > Is there consensus on what the semantics should be? > > Any suggestions as to how to move forward? In the abstract, I think the EDG semantics make the most sense. I think the VC++ semantics are plausible, but extreme. I think the various in-between states that GCC has adopted over the years are just woefully inconsistent. So, my first preference would be to adopt the EDG semantics, and my second preference would be to adopt the VC++/trunk semantics. My preference for the EDG semantics is somewhat offset to some extent by the fact that our current semantics are "more like" GCC's past behavior, i.e., they are more backward-compatible. One of the reasons I do not like the VC++/trunk semantics are that using assert-like macros will change behavior. For example: #define check(X, Y) (X) ? (Y) : abort() check (condition, vobj = 3); I think it's quite surprising that this generates a *read* from vobj, even though: vobj = 3; does not. I would prefer not to insert reads except where absolutely required because (a) they may change the state of hardware in surprising ways, and (b) they cost cycles. In short, by requiring rereads, we're setting GCC up to underperform competing compilers on embedded systems.
On 07/16/10 16:20, Mark Mitchell wrote: > One of the reasons I do not like the VC++/trunk semantics are that using > assert-like macros will change behavior. For example: > > #define check(X, Y) (X) ? (Y) : abort() > check (condition, vobj = 3); > > I think it's quite surprising that this generates a *read* from vobj, > even though: This is my composability argument. With the reread semantics, we can't tell whether tmp = Frob (&volatile_thing, val); and Frob (&volatile_thing, val); have the same behaviour, unless we know that Frob is not a macro. > I would prefer not to insert reads except where absolutely required > because (a) they may change the state of hardware in surprising ways, > and (b) they cost cycles. In short, by requiring rereads, we're setting > GCC up to underperform competing compilers on embedded systems. I'd not thought of #b. nathan
On 07/16/10 16:20, Mark Mitchell wrote: > Nathan Sidwell wrote: > >> Is there consensus on what the semantics should be? >> >> Any suggestions as to how to move forward? > > In the abstract, I think the EDG semantics make the most sense. I > think the VC++ semantics are plausible, but extreme. I think the > various in-between states that GCC has adopted over the years are just > woefully inconsistent. > > So, my first preference would be to adopt the EDG semantics, and my > second preference would be to adopt the VC++/trunk semantics. My > preference for the EDG semantics is somewhat offset to some extent by > the fact that our current semantics are "more like" GCC's past behavior, > i.e., they are more backward-compatible. There seems to have been no responses to your suggestion. Can we move forward to reviewing the updated patch itself? The patch is http://gcc.gnu.org/ml/gcc-patches/2010-06/msg02145.html and Richard indicated he'd review it once semantics were agreed. nathan
Nathan Sidwell wrote: >> So, my first preference would be to adopt the EDG semantics, and my >> second preference would be to adopt the VC++/trunk semantics. My >> preference for the EDG semantics is somewhat offset to some extent by >> the fact that our current semantics are "more like" GCC's past behavior, >> i.e., they are more backward-compatible. > > There seems to have been no responses to your suggestion. Can we move > forward to reviewing the updated patch itself? Your patch implements *almost* the EDG semantics. As I understand it, you've implemented the EDG semantics, with one exception: that in C++, vobj5 in this table: http://gcc.gnu.org/wiki/VolatileAccessComparison is reread. In that way, you're consistent between C and C++, whereas EDG, in C++, avoids the read. I think it's better to be consistent between C and C++. Real users expect to be able to move code back and forth between the languages, and this is a very subtle difference. > The patch is http://gcc.gnu.org/ml/gcc-patches/2010-06/msg02145.html > and Richard indicated he'd review it once semantics were agreed. I think the patch is ready for review. If Richard isn't able to review it soon, I will do so. I do think that we should have a section in our manual that documents these semantics. There are always corner cases, but I think that something like the following is a reasonable start: == GCC never generates a read from a `volatile' object merely because that object is being modified, unless the object is a bitfield. (If the object is a bitfield, the compiler may have to read the current value of the bitfield, modify it, and store the new value.) Concretely, none of the following statements result in a read of `vobj': int obj; volatile int vobj; vobj = 0; obj = vobj = 0; obj ? vobj = 0 : vobj = 1; == Would you please find a place to put that (or some modified form thereof) in the manual? I can review that in parallel with Richard reviewing the core patch. Thanks,
On Wed, Aug 18, 2010 at 5:30 PM, Mark Mitchell <mark@codesourcery.com> wrote: > Nathan Sidwell wrote: > >>> So, my first preference would be to adopt the EDG semantics, and my >>> second preference would be to adopt the VC++/trunk semantics. My >>> preference for the EDG semantics is somewhat offset to some extent by >>> the fact that our current semantics are "more like" GCC's past behavior, >>> i.e., they are more backward-compatible. >> >> There seems to have been no responses to your suggestion. Can we move >> forward to reviewing the updated patch itself? > > Your patch implements *almost* the EDG semantics. As I understand it, > you've implemented the EDG semantics, with one exception: that in C++, > vobj5 in this table: > > http://gcc.gnu.org/wiki/VolatileAccessComparison > > is reread. In that way, you're consistent between C and C++, whereas > EDG, in C++, avoids the read. > > I think it's better to be consistent between C and C++. Real users > expect to be able to move code back and forth between the languages, and > this is a very subtle difference. > >> The patch is http://gcc.gnu.org/ml/gcc-patches/2010-06/msg02145.html >> and Richard indicated he'd review it once semantics were agreed. > > I think the patch is ready for review. If Richard isn't able to review > it soon, I will do so. The patch is ok, and we indeed should document these semantics. Thanks, Richard. > I do think that we should have a section in our manual that documents > these semantics. There are always corner cases, but I think that > something like the following is a reasonable start: > > == > GCC never generates a read from a `volatile' object merely because that > object is being modified, unless the object is a bitfield. (If the > object is a bitfield, the compiler may have to read the current value of > the bitfield, modify it, and store the new value.) > > Concretely, none of the following statements result in a read of `vobj': > > int obj; > volatile int vobj; > vobj = 0; > obj = vobj = 0; > obj ? vobj = 0 : vobj = 1; > == > > Would you please find a place to put that (or some modified form > thereof) in the manual? I can review that in parallel with Richard > reviewing the core patch. > > Thanks, > > -- > Mark Mitchell > CodeSourcery > mark@codesourcery.com > (650) 331-3385 x713 >
On Aug 18, 2010, at 9:00 AM, Richard Guenther wrote: >> I think the patch is ready for review. If Richard isn't able to review >> it soon, I will do so. > > The patch is ok, and we indeed should document these semantics. Yes, please let's document the chosen semantics. Would be nice to push the semantics into other compilers (clang, Visual C++) and the C and C++ standards groups as well. Here's to hoping that everyone can agree...
On Fri, Aug 13, 2010 at 2:41 AM, Nathan Sidwell <nathan@codesourcery.com> wrote: > On 07/16/10 16:20, Mark Mitchell wrote: >> >> Nathan Sidwell wrote: >> >>> Is there consensus on what the semantics should be? >>> >>> Any suggestions as to how to move forward? >> >> In the abstract, I think the EDG semantics make the most sense. I >> think the VC++ semantics are plausible, but extreme. I think the >> various in-between states that GCC has adopted over the years are just >> woefully inconsistent. >> >> So, my first preference would be to adopt the EDG semantics, and my >> second preference would be to adopt the VC++/trunk semantics. My >> preference for the EDG semantics is somewhat offset to some extent by >> the fact that our current semantics are "more like" GCC's past behavior, >> i.e., they are more backward-compatible. > > There seems to have been no responses to your suggestion. Can we move > forward to reviewing the updated patch itself? > > The patch is http://gcc.gnu.org/ml/gcc-patches/2010-06/msg02145.html > and Richard indicated he'd review it once semantics were agreed. > Your patch caused: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45361
On 08/20/10 18:32, H.J. Lu wrote: > > Your patch caused: > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45361 Is the behaviour of that test case changed by the gimple change? what is the assembly output you are seeing -- is it merely regex tuning? I.e. is this a regression, or is it lack of progression? nathan
2010-06-21 Nathan Sidwell <nathan@codesourcery.com> gcc/ * gimplify.c (gimplify_modify_expr): When assigning to volatiles, copy the src value and return a copy. gcc/testsuite/ * gcc.target/i386/volatile-2.c: New. Index: gimplify.c =================================================================== --- gimplify.c (revision 160876) +++ gimplify.c (working copy) @@ -4467,6 +4467,28 @@ gcc_assert (TREE_CODE (*expr_p) == MODIFY_EXPR || TREE_CODE (*expr_p) == INIT_EXPR); + if (want_value && TREE_THIS_VOLATILE (TREE_TYPE (*to_p))) + { + /* copy the rhs to a temporary, store the temporary and then + return it. This makes sure we consistently do not re-read a + volatile just after storing it. */ + tree lhs = *to_p; + tree rhs = *from_p; + tree nv_type = TYPE_MAIN_VARIANT (TREE_TYPE (lhs)); + tree tmp = create_tmp_var (nv_type, "modtmp"); + tree tmp_ass = build2 (MODIFY_EXPR, nv_type, tmp, rhs); + tree vol_ass = build2 (MODIFY_EXPR, nv_type, lhs, tmp); + + recalculate_side_effects (tmp_ass); + gimplify_and_add (tmp_ass, pre_p); + + recalculate_side_effects (vol_ass); + gimplify_and_add (vol_ass, pre_p); + + *expr_p = tmp; + return GS_OK; + } + /* Insert pointer conversions required by the middle-end that are not required by the frontend. This fixes middle-end type checking for for example gcc.dg/redecl-6.c. */ Index: testsuite/gcc.target/i386/volatile-2.c =================================================================== --- testsuite/gcc.target/i386/volatile-2.c (revision 0) +++ testsuite/gcc.target/i386/volatile-2.c (revision 0) @@ -0,0 +1,92 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +/* Check volatiles are written, read or not re-read consistently */ + + +/* simple assignments */ + +extern int volatile obj_0; +void test_0 (int data) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_0" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_0," } } */ + obj_0 = data; +} + +extern int volatile obj_1; +int test_1 (int data) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_1" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_1," } } */ + return obj_1 = data; +} + +extern int volatile obj_2; +int test_2 (void) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_2" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_2," } } */ + return obj_2 = 0; +} + + +/* Assignments in compound exprs */ + +extern int volatile obj_3; +int test_3 (int data) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_3" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_3," } } */ + return (obj_3 = data, 0); +} + +extern int volatile obj_4; +int test_4 (void) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_4" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_4," } } */ + return (obj_4 = 0, 0); +} +extern int volatile obj_5; +int test_5 (void) +{ + /* should reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_5" } } */ + /* { dg-final { scan-assembler "movl\[ \t\]obj_5," } } */ + return (obj_5 = 0, obj_5); +} + +/* Assignments in conditional exprs */ + +extern int volatile obj_6; +void test_6 (int data, int cond) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_6" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_6," } } */ + cond ? obj_6 = data : 0; +} + +extern int volatile obj_7; +int test_7 (int data, int cond) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_7" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_7," } } */ + return cond ? obj_7 = data : 0; +} + +extern int volatile obj_8; +int test_8 (int cond) +{ + /* should not reread obj */ + /* { dg-final { scan-assembler "movl\[ \t\]\[^,\]+, obj_8" } } */ + /* { dg-final { scan-assembler-not "movl\[ \t\]obj_8," } } */ + return cond ? obj_8 = 0 : 0; +}