Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Re: Pointer Arithmetic & UB

Reply
Thread Tools

Re: Pointer Arithmetic & UB

 
 
88888 Dihedral
Guest
Posts: n/a
 
      01-18-2013
Ben Bacarisse於 2013年1月16日星期三UTC+8下午11時34分52 寫道:
> Tim Rentsch <(E-Mail Removed)> writes:
>
>
>
> > Ben Bacarisse <(E-Mail Removed)> writes:

>
> >

>
> >> Tim Rentsch <(E-Mail Removed)> writes:

>
> >>

>
> >>> Ben Bacarisse <(E-Mail Removed)> writes:

>
> >>>

>
> >>>> Tim Rentsch <(E-Mail Removed)> writes:

>
> >>>>>

>
> >>>>> [discussing the "other unknown side effects" related to volatile

>
> >>>>> objects] They might modify the object being read, or they might

>
> >>>>> not; they might modify some other scalar object, or they might

>
> >>>>> not;

>
> >>>>

>
> >>>> [Aside: could it? Only if it, too, is volatile I'd have thought.]

>
> >>>

>
> >>> The Standard says only that there may be other unknown side

>
> >>> effects. AFAIK the Standard puts no limitations on what those

>
> >>> other side effects might be, so they could include changes to other

>
> >>> objects, even non-volatile ones.

>
> >>

>
> >> Footnote 34 on 6.2.4 p2 seems to suggest otherwise, does it not?

>
> >>

>
> >> 2. An object exists, has a constant address, and retains

>
> >> its last-stored value throughout its lifetime.[34]

>
> >>

>
> >> 34) In the case of a volatile object, the last store need not be

>
> >> explicit in the program.

>
> >>

>
> >

>
> > An object declared volatile is allowed to change at any time,

>
> > with no explicit program actions. That's why this footnote

>
> > is there.

>
>
>
> My point is that it's worded in a very odd way. It might also say:
>
>
>
> 34) In the case of a program that declares at least one volatile
>
> object, the last store on *any* object need not be explicit in the
>
> program.
>
>
>
> Why is the current wording so very conservative? The retraining of an
>
> object's explicitly last-stored value does not apply to any object in a
>
> program that has at least one volatile, so it seems to be a rather
>
> measly clarification.
>
>
>
> > More significantly, this paragraph (and indeed almost all of the

>
> > Standard) is concerned only with what an implementation may do (or

>
> > not do). The consequences of accessing a volatile are outside the

>
> > control of, and even the knowledge of, the implementation. If

>
> > there is text in the Standard that is meant to impose limitations

>
> > outside of the domain of just implementations, that distinction

>
> > would have to be made apparent in the text. I just don't know of

>
> > any such cases (or at least I can't remember any).

>
>
>
> I don't have any trouble with that interpretation but it sets up a
>
> tension. The standard doesn't want to restrict what an implementation
>
> does in the presence of volatile accesses, but it also wants to
>
> document what is likely to happen with implementations that define more
>
> normal behaviour for volatile objects. The result is some rather coy
>
> wording.
>
>
>
> --
>
> Ben.

A volatile object is related with multi-thread and multi-core systems that a program can't assume
that the program is the only modifier of the object.

But most people are not writing programs for
the above situations now.
 
Reply With Quote
 
 
 
 
James Kuyper
Guest
Posts: n/a
 
      01-18-2013
On 01/18/2013 03:59 PM, glen herrmannsfeldt wrote:
> Tim Rentsch <(E-Mail Removed)> wrote:

....
>> ... It's like
>> saying an implementation could define an extension where array
>> indexing only works for even index values -- it cannot, because
>> such an implementation fails to meet the requirements that the
>> Standard otherwise imposes.

>
> Probably a bad example because, with alignment restrictions,
> implementations can do just that. The implementation has to
> add the appropriate padding, as required.


Could you explain that in a little more detail? Could you show a case
where the standard permits an implementation to treat array[i] as
problematic solely because i is odd, and not for any other reason?
 
Reply With Quote
 
 
 
 
Tim Rentsch
Guest
Posts: n/a
 
      01-18-2013
Keith Thompson <(E-Mail Removed)> writes:

> Tim Rentsch <(E-Mail Removed)> writes:
>> Richard Damon <(E-Mail Removed)> writes:

> [...]
>>> Also, I don't believe that an implementation can fail to translate
>>> something like
>>>
>>> struct foo {
>>> int i;
>>> volatile int v;
>>> } myfoo;
>>>
>>> ...
>>>
>>> myfoo.v = 1;
>>>
>>> (i.e., a believe that it is allowed to arbitrarily make give any object
>>> a volatile modifier).

>>
>> If the implementation can prove that the assignment must be
>> evaluated, then strictly speaking the program doesn't have
>> to be accepted, because it is not strictly conforming. Not
>> counting that technicality though this is right.

>
> Are you saying that an implementation is free to reject a program just
> because it's not strictly conforming?
>
> 4p6 (quoting N1570) does say:
>
> A *conforming hosted implementation* shall accept any strictly
> conforming program. A *conforming freestanding implementation*
> shall accept any strictly conforming program in which [SNIP]
>
> which could be taken to imply that an implementation needn't accept a
> program that's not strictly conforming.
>
> But paragraph 3 says:
>
> A program that is correct in all other aspects, operating on correct
> data, containing unspecified behavior shall be a correct program
> and act in accordance with 5.1.2.3.
>
> A program can't "act in accordance with 5.1.2.3" if the implementation
> doesn't accept it.
>
> A concrete example: I don't believe a conforming hosted implementation
> may reject this:
>
> #include <stdio.h>
> #include <limits.h>
> int main(void) {
> printf("INT_MAX = %d\n", INT_MAX);
> return 0;
> }
>
> even though it's not strictly conforming.


In fact I do believe that implementations are obliged to accept
only those programs that are strictly conforming, and may choose
not to accept (or "reject", if that means the same as not accept)
any programs that aren't.

I think the point of section 4 p3 is only to say that unspecified
behavior is not the same as undefined behavior (ie, unspecified
behavior is not completely constrained, but it does have limits,
and those limits must be observed).

I also think a not-completely-unreasonable argument supporting
that viewpoint (ie, that NSC programs need not be accepted) could
be made based on either (a) the exempting phrase "correct in all
other aspects", or (b) the phrasing used for a program "operating
on correct data", because programs that have not been accepted
won't be running and so they won't be operating on any data,
either correct or otherwise.

In practical terms though I don't think it matters much either
way. No sensible implementor is going to choose rejecting a
program just because it happens not to be strictly conforming, and
any implementation that does so either is not conforming or has a
QOI so low that no one would take it seriously. It's simply a
possible technical objection, and that's really all I was trying
to say, that possibly there was a technical inaccuracy.
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-18-2013
On 1/18/2013 14:13, Tim Rentsch wrote:
> glen herrmannsfeldt <(E-Mail Removed)> writes:
>
>> Tim Rentsch <(E-Mail Removed)> wrote:
>> [snip]
>>
>>> and implementations aren't allowed to assume something (like
>>> what side effects are caused by some volatile access) that the
>>> Standard deems outside of what they know.

>>
>> This is the one I don't agree with. An implementation might, for
>> example, and as an extension, allow access to I/O registers. The
>> implementation might, then, document that I/O registers should be
>> declared volatile, and might even document that no other kinds of
>> volatile data are supported.

>
> Implementations don't get to make that choice. The volatile
> keyword is allowed in declaring/defining any variable, and also
> in type names (eg, casts or compound literals). Any expression
> doing an access through a volatile-qualified lvalue is obliged to
> perform that access under the rules that the Standard gives for
> the semantics of such accesses. Implementations don't get to
> choose which volatile-qualified accesses are "supported" and
> which ones aren't -- the Standard obliges them to perform them
> all, and without making any assumptions about what side effects
> might take place as a result.
>
>>> What constitutes an access to a volatile object is
>>> implementation defined, but what happens when a volatile
>>> access occurs is not, and cannot be,

>>
>> Why can't the implementation define exactly what types of
>> volatile access it allows?

>
> Because it isn't consistent with the requirements that all
> accesses be performed, and volatile-qualified accesses be
> performed according to how the Standard describes the semantics
> for such accesses.
>
>>> defined by an implementation, because the Standard specifically
>>> allows side effects unknown to the implementation to take place,
>>> and implementations are obliged to proceed under that assumption.

>>
>> OK, then, the implementation can, as an extension to the standard,
>> define which types of volatile access it allow and which ones it
>> doesn't. [snip]

>
> That simply isn't consistent with what the Standard requires.
> Implementations must carry out volatile-qualified accesses
> according to the requireed semantics; they don't get to choose
> which of those might be "allowed" and which aren't. It's like
> saying an implementation could define an extension where array
> indexing only works for even index values -- it cannot, because
> such an implementation fails to meet the requirements that the
> Standard otherwise imposes.
>


GCC has this to say[1]:

"...

volatile int *src = somevalue;
*src;

...

If it is a scalar type, or on most targets an aggregate type whose
only member object is of a scalar type, or a union type whose member
objects are of scalar types, the expression is interpreted by GCC as a
read of the volatile object; in the other cases, the expression is only
evaluated for its side-effects."

Which possible "side-effects" could they be referring to, if they choose
not to read the volatile object for the non-scalar case?

[1]
http://gcc.gnu.org/onlinedocs/gcc/Qu...implementation

--
- Shao Miller
--
"Thank you for the kind words; those are the kind of words I like to hear.

Cheerily," -- Richard Harter
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      01-19-2013
glen herrmannsfeldt <(E-Mail Removed)> writes:

> Tim Rentsch <(E-Mail Removed)> wrote:
>> glen herrmannsfeldt <(E-Mail Removed)> writes:

>
>>> Tim Rentsch <(E-Mail Removed)> wrote:
>>> [snip]

>
>> Implementations don't get to make that choice. The volatile
>> keyword is allowed in declaring/defining any variable, and also
>> in type names (eg, casts or compound literals). Any expression
>> doing an access through a volatile-qualified lvalue is obliged to
>> perform that access under the rules that the Standard gives for
>> the semantics of such accesses. Implementations don't get to
>> choose which volatile-qualified accesses are "supported" and
>> which ones aren't -- the Standard obliges them to perform them
>> all, and without making any assumptions about what side effects
>> might take place as a result.

>
> I don't say this very often, but that is just dumb.
>
> Except for the case of multithreading, which has been pretty
> much left out of the discussion, only the implementation knows
> which possible accesses need to be protected against, and how
> to protect against them. [snip]


Probably you are operating under an assumption that is
essentially wrong, namely, that the term 'implementation'
includes things like the operating system and hardware that
the programs will run on. It doesn't.

> Volatile pointers and pointers (that aren't volatile) to
> volatile locations also have been pretty much left out of the
> discussion.


Volatile objects that contain pointers are accessed in the same
way that volatile objects containing non-pointers are.

Pointers to volatile locations are covered under the term
'volatile-qualified lvalue', which I already discussed.

>>> Why can't the implementation define exactly what types of
>>> volatile access it allows?

>>
>> Because it isn't consistent with the requirements that all
>> accesses be performed, and volatile-qualified accesses be
>> performed according to how the Standard describes the semantics
>> for such accesses.

>
> There are way too many different ways to access memory, most of
> which only the implementation can know about. [snip example]
> Now, say that the implementation has a cache such that, even
> though memory fetch instructions are executed it never actually
> gets to real memory.


Here again you appear to be confused about what is included
under the term 'implementation'.

> Maybe the user need to go to the BIOS setup and turn off
> caching. Yet you say that the standard understands how to do
> that?


No, I said no such thing. The Standard explicitly does _not_
specify such things, and in effect requires implementations
not to make any assumptions about what will happen when a
volatile-qualified access is done.


>> [snip] It's like
>> saying an implementation could define an extension where array
>> indexing only works for even index values -- it cannot, because
>> such an implementation fails to meet the requirements that the
>> Standard otherwise imposes.

>
> Probably a bad example because, with alignment restrictions,
> implementations can do just that. The implementation has to
> add the appropriate padding, as required.


I expect you have misunderstood my example. I'm not talking about
byte addresses but about the values used in index expressions. If
we have a declaration like

int a[10];

then all of these expressions

a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]

are necessarily legal expressions, and must designate the
corresponding element of the array a. Questions about
alignment or padding have no bearing on the issue.
 
Reply With Quote
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      01-19-2013
Tim Rentsch <(E-Mail Removed)> wrote:

(snip)
>>> Implementations don't get to make that choice. The volatile
>>> keyword is allowed in declaring/defining any variable, and also
>>> in type names (eg, casts or compound literals). Any expression
>>> doing an access through a volatile-qualified lvalue is obliged to
>>> perform that access under the rules that the Standard gives for
>>> the semantics of such accesses. Implementations don't get to
>>> choose which volatile-qualified accesses are "supported" and
>>> which ones aren't -- the Standard obliges them to perform them
>>> all, and without making any assumptions about what side effects
>>> might take place as a result.


>> I don't say this very often, but that is just dumb.


>> Except for the case of multithreading, which has been pretty
>> much left out of the discussion, only the implementation knows
>> which possible accesses need to be protected against, and how
>> to protect against them. [snip]


> Probably you are operating under an assumption that is
> essentially wrong, namely, that the term 'implementation'
> includes things like the operating system and hardware that
> the programs will run on. It doesn't.


OK, what I was callying implementation is a specific
compiler/hardware/OS combination.

Well, it used to be that compilers were written by hardware
manufacturers specifically for their hardware. That doesn't
happen so often anymore.

So, yes, gcc running on different systems may contain
most of the same source code. Now, for a large fraction of
the features of the language the same compiler code can be used
for almost all systems. (What do you call a specific
hardware/OS/whatever-else combination that a compiler must
targer? Implementations didn't seem so far off to me.)

At some point, you have to get down to actual machine
instructions for the target hardware, and system calls
to the target OS. In many cases, the hardware differences
can be parameterized, such as the number of available
registers, and again common code can be used.

And even more, there are some uses for volatile that might
be pretty much the same for different hardware and/or OS
combinations. But maybe not all.

As I mentioned, some hardware might have an interlocked
storage update system, and it might be that is needed for
some particular implementation (that is, hardware and/or OS).
Or many more things that I haven't thought about.

Now, besides portable compilers like gcc and lcc there are
still many that only exist for one hardware and OS.
Such might even have specific features for that target
to help convince users to buy one. That might include
support for hardware or software features not supported
by the more portable compilers, and some of those might be
related to the need for volatile that don't exist on other
systems.

OK, so a long way to explain what I tried to mean by
implementation. Either a specific purpose compiler, or
a general (portable) compiler compiled for a specific
hardware/OS combination.

But also, as I don't seem to have explained very well, there
might be reasons for specific hardware and/or OS to need
something to be volatile, where other hardware and/or OS
have no similar need.

>> Volatile pointers and pointers (that aren't volatile) to
>> volatile locations also have been pretty much left out of the
>> discussion.


> Volatile objects that contain pointers are accessed in the same
> way that volatile objects containing non-pointers are.


> Pointers to volatile locations are covered under the term
> 'volatile-qualified lvalue', which I already discussed.


OK, I missed that one.

>>>> Why can't the implementation define exactly what types of
>>>> volatile access it allows?


>>> Because it isn't consistent with the requirements that all
>>> accesses be performed, and volatile-qualified accesses be
>>> performed according to how the Standard describes the semantics
>>> for such accesses.


>> There are way too many different ways to access memory, most of
>> which only the implementation can know about. [snip example]
>> Now, say that the implementation has a cache such that, even
>> though memory fetch instructions are executed it never actually
>> gets to real memory.


> Here again you appear to be confused about what is included
> under the term 'implementation'.


>> Maybe the user need to go to the BIOS setup and turn off
>> caching. Yet you say that the standard understands how to do
>> that?


> No, I said no such thing. The Standard explicitly does _not_
> specify such things, and in effect requires implementations
> not to make any assumptions about what will happen when a
> volatile-qualified access is done.


But not making any assumptions IS making an assumption.
It makes the assumption that the specific hardware in
question behaves in the way that the standard expects.

Now, in the case of an expression like i+j, and to be
more specific i and j are not volatile, we have a pretty
good idea what to expect. That the hardware has some ability
to add two values. That is a pretty good assumption for
most hardware.

But say the hardware has some new feature that no hardware
had before, and that requires something special from the
compiler. Even more, say that the compiler is written
specifically for that hardware. Now, say this feature
allows certain memory locations (or maybe locations
that aren't memory) to change at unexpected times.
Sure sounds like volatile would be a good choice.
But maybe it doesn't work exactly the way the standard
says, for some strange reason. And even more, that there
might not be any other use for volatile. It sure would
be convenient if volatile could be used in this case.

I suppose the other choice is to invent some new keyword,
add it to the compiler, and confuse everyone.

(snip of alignment discussion)

-- glen
 
Reply With Quote
 
Richard Damon
Guest
Posts: n/a
 
      01-19-2013
On 1/18/13 1:08 PM, Tim Rentsch wrote:
> Richard Damon <(E-Mail Removed)> writes:
>
>> [snip] A program
>> is allowed to take the address of and object of type T, convert that T*
>> pointer (implicitly) to a volatile T*, and then dereference it with
>> defined behavior. (similar to converting a T* to a const T*).
>>
>> This implies that the access through a pointer to volatile needs to be
>> somewhat similar in effect as a non-volatile access. Since, in general,
>> the implementation can't know when passed a pointer to volatile if the
>> object pointer to is truly volatile or not it becomes hard to make the
>> definition of access dependent on that.

>
> This idea isn't right. An expression that accesses an object is
> subject to volatile-access semantics exactly when the lvalue that
> designates the object is volatile-qualified. Whether the object
> actually being accessed was declared volatile or not makes no
> difference (as far as how this particular access must be performed).
>


6.7.3.7 says:
An object that has volatile-qualified type may be modified in ways
unknown to the implementation or have other unknown side effects.
Therefore any expression referring to such an object shall be evaluated
strictly according to the rules of the abstract machine, as described in
5.1.2.3. Furthermore, at every sequence point the value last stored in
the object shall agree with that prescribed by the abstract machine,
except as modified by the unknown factors mentioned previously.134) What
constitutes an access to an object that has volatile-qualified type is
implementation-defined.

I read the standard as saying it is the volatile-qualified *object* that
is subject to being modified in ways unknown to the implementation.

Thus for the program snippit:

int i = 0;
int j;
volatile int* p = &i;
j = *p;

The fact that *p has a volatile qualification on it does NOT give the
implementation the right to not fetch the value that was stored in i. It
may do some extra actions so as to make sure that it gets the "current"
value of i,

>> Also, I don't believe that an implementation can fail to translate
>> something like
>>
>> struct foo {
>> int i;
>> volatile int v;
>> } myfoo;
>>
>> ...
>>
>> myfoo.v = 1;
>>
>> (i.e., a believe that it is allowed to arbitrarily make give any object
>> a volatile modifier).

>
> If the implementation can prove that the assignment must be
> evaluated, then strictly speaking the program doesn't have
> to be accepted, because it is not strictly conforming. Not
> counting that technicality though this is right.


Per the standard,
A strictly conforming program shall use only those features of the
language and library specified in this International Standard.3) It
shall not produce output dependent on any unspecified, undefined, or
implementation-defined behavior, and shall not exceed any minimum
implementation limit.

This snippet does NOT violate any of those conditions. The behavior of
myfoo.v = 1 IS defined by the standard, what is implementation defined
is if this constitutes an "access" to a volatile-qualified object, and
thus might be modified beyond the knowledge of the implementation.

>
>> What the implementation would be allowed to do (I believe) is to reserve
>> certain "addresses" (values of pointers) as something special, and only
>> decode that "specialness" for volatiles, thus normal pointer access
>> would not be slowed down (but not "work" for these addresses, but
>> volatile pointer access to these special addresses could be converted to
>> a different instruction to access a different I/O space.

>
> Any access arising from a volatile-qualified lvalue must be
> performed using volatile-access semantics, whether the address
> is "in range" or not. Unless there is an actual declaration for
> a variable (aka object) at such an address, and which includes a
> volatile qualifier, then the implementation must allow access
> through an lvalue that is not volatile-qualified as well as a
> volatile-qualified one. Moreover the non-volatile lvalues must
> access the same area of memory as the volatile lvalues.
>


The standard talks of volatile object, not volatile lvalues. It appears
that if the implementation can prove that the object behing the volatile
lvalue is not actually volatile, then by the "as-if" rule, it could skip
any volatile specific operations on the access.

My comments about "in range" was dealing with the concept that an
implementation might use volatile to encode that a address was in "I/O
space" as opposed to "memory space". If this implies that placing the
address of a "normal" variable into a volatile pointer and accessing
does not get access the variable, then that is non-conforming. If the
"I/O space" is smaller than the addressable memory space, then it would
be allowed for the implementation to reserve some of the memory space
(where it will never put an object) as an alias for "I/O space", and on
a volatile access to that memory range, use the I/O space access
instruction instead of the memory access instruction.

 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      01-20-2013
Richard Damon <(E-Mail Removed)> writes:

> On 1/18/13 1:08 PM, Tim Rentsch wrote:
>> Richard Damon <(E-Mail Removed)> writes:
>>
>>> [snip] A program
>>> is allowed to take the address of and object of type T, convert that T*
>>> pointer (implicitly) to a volatile T*, and then dereference it with
>>> defined behavior. (similar to converting a T* to a const T*).
>>>
>>> This implies that the access through a pointer to volatile needs to be
>>> somewhat similar in effect as a non-volatile access. Since, in general,
>>> the implementation can't know when passed a pointer to volatile if the
>>> object pointer to is truly volatile or not it becomes hard to make the
>>> definition of access dependent on that.

>>
>> This idea isn't right. An expression that accesses an object is
>> subject to volatile-access semantics exactly when the lvalue that
>> designates the object is volatile-qualified. Whether the object
>> actually being accessed was declared volatile or not makes no
>> difference (as far as how this particular access must be performed).

>
> 6.7.3.7 says:
> An object that has volatile-qualified type may be modified in
> ways unknown to the implementation or have other unknown side
> effects. Therefore any expression referring to such an object
> shall be evaluated strictly according to the rules of the
> abstract machine, as described in 5.1.2.3. Furthermore, at
> every sequence point the value last stored in the object shall
> agree with that prescribed by the abstract machine, except as
> modified by the unknown factors mentioned previously.134) What
> constitutes an access to an object that has volatile-qualified
> type is implementation-defined.
>
> I read the standard as saying it is the volatile-qualified
> *object* that is subject to being modified in ways unknown to
> the implementation.
>
> Thus for the program snippit:
>
> int i = 0;
> int j;
> volatile int* p = &i;
> j = *p;
>
> The fact that *p has a volatile qualification on it does NOT
> give the implementation the right to not fetch the value that
> was stored in i.


I see, I partly misunderstood what you were saying. This statement
is right -- using a volatile-qualified lvalue to do an access has
to work even the declared type of the object being read or written
is not volatile-qualified (ie, if the declaration doesn't specify
volatile).

> It may do some extra actions so as to make sure that it gets
> the "current" value of i,


Here is the key part of what I thought you were talking about.
In fact the implementation is obliged to access i (when '*p' is
evaluated) in exactly the same way as if 'i' had been declared
volatile.

>>> Also, I don't believe that an implementation can fail to translate
>>> something like
>>>
>>> struct foo {
>>> int i;
>>> volatile int v;
>>> } myfoo;
>>>
>>> ...
>>>
>>> myfoo.v = 1;
>>>
>>> (i.e., a believe that it is allowed to arbitrarily make give any object
>>> a volatile modifier).

>>
>> If the implementation can prove that the assignment must be
>> evaluated, then strictly speaking the program doesn't have
>> to be accepted, because it is not strictly conforming. Not
>> counting that technicality though this is right.

>
> Per the standard,
> A strictly conforming program shall use only those features of the
> language and library specified in this International Standard.3) It
> shall not produce output dependent on any unspecified, undefined, or
> implementation-defined behavior, and shall not exceed any minimum
> implementation limit.
>
> This snippet does NOT violate any of those conditions.


It does, because the output can depend on implementation-defined
behavior.

> The behavior of myfoo.v = 1 IS defined by the standard, what is
> implementation defined is if this constitutes an "access" to a
> volatile-qualified object, and thus might be modified beyond
> the knowledge of the implementation.


First of all you have misunderstood. The phrase "What constitutes
an access to an object that has volatile-qualified type" doesn't
mean _which expressions count_ as a volatile access, but _what
actions are to be performed_ when a volatile-qualified lvalue
expression is used to access an object. Implementations don't get
to decide which expressions are subject to the rules of
volatile-qualified access -- the Standard does that. What they do
get to decide is what machine-level instructions are used to
carry out such accesses.

Second, the assertion that "The behavior of myfoo.v = 1 IS defined
by the standard" is wrong. The Standard specifically says that
objects with volatile-qualifed type "may have other unknown side
effects." The Standard places no limits on what these unknown
side effects might be, and it certainly doesn't define the behavior
of what happens when they occur.


>>> What the implementation would be allowed to do (I believe) is
>>> to reserve certain "addresses" (values of pointers) as
>>> something special, and only decode that "specialness" for
>>> volatiles, thus normal pointer access would not be slowed down
>>> (but not "work" for these addresses, but volatile pointer
>>> access to these special addresses could be converted to a
>>> different instruction to access a different I/O space.

>>
>> Any access arising from a volatile-qualified lvalue must be
>> performed using volatile-access semantics, whether the address
>> is "in range" or not. Unless there is an actual declaration for
>> a variable (aka object) at such an address, and which includes a
>> volatile qualifier, then the implementation must allow access
>> through an lvalue that is not volatile-qualified as well as a
>> volatile-qualified one. Moreover the non-volatile lvalues must
>> access the same area of memory as the volatile lvalues.

>
> The standard talks of volatile object, not volatile lvalues. It
> appears that if the implementation can prove that the object
> behing the volatile lvalue is not actually volatile, then by the
> "as-if" rule, it could skip any volatile specific operations on
> the access.


The Standard does use the term 'volatile object', but 6.7.3 p7
uses the phrase 'An object that has volatile-qualified type'.
The type here is the type of the lvalue used to do the access,
not the type used in declaring the object (which declaration
may not even exist). For the latter notion, the Standard uses
the term 'declared type'.

To see this is right, consider an example like

volatile int *port_status = (void*) 0x200;

Any accesses through *port_status must observe volatile-access
semantics just the same as any other volatile access. Another
example might use a volatile-qualified lvalue to access an
object in allocated memory, which certainly couldn't have been
declared volatile, but may be important to access using volatile
semantics because the object is in memory shared with another
process.

> My comments about "in range" was dealing with the concept that
> an implementation might use volatile to encode that a address
> was in "I/O space" as opposed to "memory space". If this
> implies that placing the address of a "normal" variable into a
> volatile pointer and accessing does not get access the variable,
> then that is non-conforming. If the "I/O space" is smaller than
> the addressable memory space, then it would be allowed for the
> implementation to reserve some of the memory space (where it
> will never put an object) as an alias for "I/O space", and on a
> volatile access to that memory range, use the I/O space access
> instruction instead of the memory access instruction.


Using a different instruction (or instructions plural) is okay,
but non-volatile accesses and volatile accesses must refer to
the same area of memory (ie, for a given address). In fact this
is basically the same as the point you made earlier with using
a volatile pointer to access a variable not declared volatile.
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      01-20-2013
glen herrmannsfeldt <(E-Mail Removed)> writes:

> Tim Rentsch <(E-Mail Removed)> wrote:
> (snip)
>
>> Probably you are operating under an assumption that is
>> essentially wrong, namely, that the term 'implementation'
>> includes things like the operating system and hardware that
>> the programs will run on. It doesn't.

>
> OK, what I was callying implementation is a specific
> compiler/hardware/OS combination. [snip]
>
> But also, as I don't seem to have explained very well, there
> might be reasons for specific hardware and/or OS to need
> something to be volatile, where other hardware and/or OS
> have no similar need. [snip]


I think you're missing a key point here. The kinds of things
you're talking about are all platform specific. Implementations
are free to define extensions that handle platform-specific
mechanisms, even very strange ones, but there's no reason those
need to involve volatile. The reason volatile exists is for
cases when a developer knows things that the implementor doesn't,
not the other way around. Hence, accessing a volatile is defined
so that no assumptions are made about what all else might be going
on, specifically because what seems like a reasonable assumption
in other cases might not be true in this case -- the developer
knows better than the implementor, and by using volatile is
telling the implementor that this is so, so the implementation
should just play dumb and do what it is told in the most direct
and straightforward way it can. Exotic platform-specific stuff,
where the implementation can take advantage of knowledge that a
developer might not have, needn't use volatile at all, but instead
can (and in many cases should) use platform-specific extensions
whose semantics is determined solely by the implementation, and
not constrained by the Standard.

The whole point of volatile is to have a standard way of letting
the implementation know that the developer knows something that
the implementor doesn't. And that is precisely what makes it
valuable.
 
Reply With Quote
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      01-21-2013
Tim Rentsch <(E-Mail Removed)> wrote:
> glen herrmannsfeldt <(E-Mail Removed)> writes:
>> Tim Rentsch <(E-Mail Removed)> wrote:
>> (snip)


(snip)
>> OK, what I was callying implementation is a specific
>> compiler/hardware/OS combination. [snip]


>> But also, as I don't seem to have explained very well, there
>> might be reasons for specific hardware and/or OS to need
>> something to be volatile, where other hardware and/or OS
>> have no similar need. [snip]


> I think you're missing a key point here. The kinds of things
> you're talking about are all platform specific. Implementations
> are free to define extensions that handle platform-specific
> mechanisms, even very strange ones, but there's no reason those
> need to involve volatile.


Well, yes, but it is sometimes necessary to have a way to tell
the implementation that you are doing something unusual.

> The reason volatile exists is for cases when a developer knows
> things that the implementor doesn't, not the other way around.


Yes, I wasn't trying to deny those, but to indicate that there
might be more than that.

> Hence, accessing a volatile is defined
> so that no assumptions are made about what all else might be going
> on, specifically because what seems like a reasonable assumption
> in other cases might not be true in this case -- the developer
> knows better than the implementor, and by using volatile is
> telling the implementor that this is so, so the implementation
> should just play dumb and do what it is told in the most direct
> and straightforward way it can.


Easy to say, not always so easy to do. That works fine if there
are only two ways to do something, but maybe there are more?

> Exotic platform-specific stuff, where the implementation can
> take advantage of knowledge that a developer might not have,
> needn't use volatile at all, but instead can (and in many
> cases should) use platform-specific extensions whose semantics
> is determined solely by the implementation, and not
> constrained by the Standard.


Well, ok, but everyone hates implementation specific keywords.
For one, there is always the possibility that one conflicts with
other uses for the word.

Now, so far the only use for volatile within what is otherwise
standard C is for multithreading, when one tread might change
a variable that another thread is using. That seems fine to me.

Anohter one mentioned is I/O registers. That is pretty much
implementation specific, but the implementation needs to know
when the developer is using I/O registers. I suppose one could
add a new keyword for it, but others have already suggested
volatile.

> The whole point of volatile is to have a standard way of letting
> the implementation know that the developer knows something that
> the implementor doesn't. And that is precisely what makes it
> valuable.


Yes. But exactly what those things are might be implementation
dependent. Seems to me that there is a very good chance that
they are. If there are three different things that the developer
might know, and need to tell the implementation about, what
do you do?

As I mentioned previously, one thing that you might need is
interlocked access to a memory location. That seems to be similar
to the need for volatile, but maybe not exactly.

-- glen
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Pointer to pointer or reference to pointer A C++ 7 07-05-2011 07:49 PM
Pointer to pointer Vs References to Pointer bansalvikrant@gmail.com C++ 4 07-02-2009 10:20 AM
passing the address of a pointer to a func that doesnt recieve a pointer-to-a-pointer jimjim C Programming 16 03-27-2006 11:03 PM
Usual Arithmetic Conversions-arithmetic expressions joshc C Programming 5 03-31-2005 02:23 AM
Pointer-to-pointer-to-pointer question masood.iqbal@lycos.com C Programming 10 02-04-2005 02:57 AM



Advertisments