By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,370 Members | 1,534 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,370 IT Pros & Developers. It's quick & easy.

Defeating Optimisation for memcmp()

P: n/a
Please consider the following code fragment. Assume UINT32 is a typedef
suitable for defining variables of 32 bits, and that ui32 is initialised.

UINT32 ui32;
/* ... */
/* assume ui32 now is holding a value in uc's range */
unsigned char uc = (unsigned char) ui32; /* tell Lint you know target
type is smaller */
cmos->uc = uc;

/* Check the CMOS write was successful */
/* NOTE: I know third arg evals to 1, but sizeof is used for readability
*/
if (memcmp(cmos->uc, &uc, sizeof(unsigned char)) != 0)
/* CMOS write failed */

I would expect a decent compiler to optimize away the memcmp(). Would you
agree that this change will ensure the memcmp is not optimized away?:

UINT32 ui32;
/* ... */
/* assume ui32 now is holding a value in uc's range */
volatile unsigned char uc = (unsigned char) ui32; /* tell Lint you know
target type is smaller */
cmos->uc = uc;
/* Check the CMOS write was successful */
if (memcmp(cmos->uc, &uc, sizeof(unsigned char)) != 0)
/* CMOS write failed */

Is a call to a library function (memcmp) less likely to be optimised away
than use of relational operators (a) when one of the operands is volatile,
and (b) when neither are volatile?

For example,

if ( cmos->uc != uc )
/* CMOS write failed */

compared to

if (memcmp(cmos->uc, &uc, sizeof(unsigned char)) != 0)
/* CMOS write failed */

Thank-you in advance.

--
Martin

Nov 10 '07 #1
Share this Question
Share on Google+
39 Replies


P: n/a

"Ben Bacarisse" wrote:
Presumably you mean '&cmos->uc'? The same typo appears several times
so I am not sure.
It's my mistake Ben - I did miss the ampersand for memcmp()'s first
argument. It should of course be as you say.

--
Martin

Nov 10 '07 #2

P: n/a
Martin wrote:
[...]
Is a call to a library function (memcmp) less likely to be optimised away
than use of relational operators (a) when one of the operands is volatile,
and (b) when neither are volatile?
The call has undefined behavior if *either* operand is
volatile. 6.7.3p5:

[...] If an attempt is made to refer to an object
defined with a volatile-qualified type through use of
an lvalue with non-volatile-qualified type, the
behavior is undefined.

There's a quibble: If memcmp is not implemented in C, it does
not use C lvalues to access anything at all and thus might
skirt the prohibition. But then there's 7.1.4p1:

If an argument to a function has an invalid value (such
as a value outside the domain of the function, or a
pointer outside the address space of the program, or a
null pointer, or a pointer to non-modifiable storage when
the corresponding parameter is not const-qualified) [...]
the behavior is undefined.

True, volatile is not mentioned. However, the list of ways in
which an argument can be invalid is prefaced by "such as," a
construct suggestive of a non-exhaustive list.

Finally, there's 6.3.2.3p2:

For any qualifier q, a pointer to a non-q-qualified type
may be converted to a pointer to the q-qualified version
of the type [...]

The point is that the conversion in the other direction is not
described as legal: You can add qualifiers in a conversion, but
you cannot subtract them. If you hand a pointer-to-volatile to
memcmp (which expects a pointer-to-const), I think the compiler
is required to issue a diagnostic -- and if it then accepts and
runs the program anyhow, all bets are off.

The solution to your problem is to qualify the "CMOS" thing
as volatile, and use an ordinary comparison:

struct {
...
volatile unsigned char uc;
...
} *cmos = ...;

cmos->uc = uc;
if (cmos->uc == uc) ...

The compiler is not permitted to optimize away any of the accesses
to cmos->uc, because the volatile qualifier declares that those
accesses have side-effects, just like calls to putchar(). The
compiler cannot "just know" that the comparison will turn out
true; it must actually perform it. That's what volatile does.

--
Eric Sosman
es*****@ieee-dot-org.invalid
Nov 10 '07 #3

P: n/a
Eric Sosman <es*****@ieee-dot-org.invalidwrites:
Martin wrote:
>[...]
Is a call to a library function (memcmp) less likely to be optimised
away than use of relational operators (a) when one of the operands
is volatile, and (b) when neither are volatile?

The call has undefined behavior if *either* operand is
volatile. 6.7.3p5:
<snip>
The point is that the conversion in the other direction is not
described as legal: You can add qualifiers in a conversion, but
you cannot subtract them.
I stand (implicitly) corrected. I should have seen this. This is why
c.l.c is so worth reading To the OP: ignore (most of) what I wrote!

--
Ben.
Nov 10 '07 #4

P: n/a

"Eric Sosman" wrote:
The solution to your problem is to qualify the "CMOS" thing
as volatile, and use an ordinary comparison:

struct {
...
volatile unsigned char uc;
...
} *cmos = ...;

cmos->uc = uc;
if (cmos->uc == uc) ...

The compiler is not permitted to optimize away any of the accesses
to cmos->uc, because the volatile qualifier declares that those
accesses have side-effects, just like calls to putchar(). The
compiler cannot "just know" that the comparison will turn out
true; it must actually perform it. That's what volatile does.

Thanks for your comments. I don't think making the cmos variable volatile is
an option.

Why can't I make uc volatile?

volatile unsigned char uc = (unsigned char) ui32;
/* ... init uc ... */
cmos->uc = uc;
if ( cmos->uc != uc )
/* error writing to CMOS */

--
Martin

Nov 11 '07 #5

P: n/a
"Martin" <martin.o_brien@[no-spam]which.netwrites:
"Eric Sosman" wrote:
> The solution to your problem is to qualify the "CMOS" thing
as volatile, and use an ordinary comparison:

struct {
...
volatile unsigned char uc;
...
} *cmos = ...;

cmos->uc = uc;
if (cmos->uc == uc) ...

The compiler is not permitted to optimize away any of the accesses
to cmos->uc, because the volatile qualifier declares that those
accesses have side-effects, just like calls to putchar(). The
compiler cannot "just know" that the comparison will turn out
true; it must actually perform it. That's what volatile does.


Thanks for your comments. I don't think making the cmos variable volatile is
an option.

Why can't I make uc volatile?

volatile unsigned char uc = (unsigned char) ui32;
/* ... init uc ... */
cmos->uc = uc;
if ( cmos->uc != uc )
/* error writing to CMOS */
Eric Sosman's comment was about using memcmp -- specifically that
passing memcmp a pointer to a volatile object is not permitted. You
can use a != test but...

Making uc volatile won't work. The compiler may assume that cmos->uc
is set as per the assignment (it need not access the object again). I
can't see a way round this other than making the object that is
actually volatile, volatile. Forcing the compiler to re-access uc to
compare it against the value that it may have squirreled away as the
assumed contents of cmos->uc will not help you.

--
Ben.
Nov 11 '07 #6

P: n/a
Eric Sosman wrote:
>
.... snip ...
>
The compiler is not permitted to optimize away any of the accesses
to cmos->uc, because the volatile qualifier declares that those
accesses have side-effects, just like calls to putchar(). The
compiler cannot "just know" that the comparison will turn out
true; it must actually perform it. That's what volatile does.
Not side-effects. The variable may 'spontaneously' change between
reads. There is no reason to insist on a write between reads.

--
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>
Try the download section.

--
Posted via a free Usenet account from http://www.teranews.com

Nov 11 '07 #7

P: n/a
>"Eric Sosman" wrote:
>> The solution to your problem is to qualify the "CMOS" thing
as volatile, and use an ordinary comparison:

struct {
...
volatile unsigned char uc;
...
} *cmos = ...;
(Or, as I tend to prefer, make the structure type's elements
ordinary, non-"volatile"-qualified, but use a volatile qualifier
on the pointer itself:

struct whatever { ... unsigned char uc; ... };
volatile struct whatever *cmos = ...;

This allows one to copy the entire data structure into ordinary
RAM, then manipulate it there without defeating optimization.)
>>cmos->uc = uc;
if (cmos->uc == uc) ...

The compiler is not permitted to optimize away any of the accesses
to cmos->uc, because the volatile qualifier declares that those
accesses have side-effects ...
(Or rather, that they "may" have side effects, and the compiler
should assume the worst.)
>"Martin" <martin.o_brien@[no-spam]which.netwrites:
>Thanks for your comments. I don't think making the cmos variable
volatile is an option.
Why not? (Neither Eric Sosman's method nor mine generally requires
much in the way of code changes.)
>Why can't I make uc volatile?
You can; it just is silly, and may well not work. (In fact, it is
only likely to work if the code happens to work with no "volatile"
qualifiers anyway. That is, adding the volatile qualifier in the
wrong place is extremely unlikely to help.)

[I am going to make a name change below, so that "uc" unambiguously
refers to cmos->uc, and use "temp_v" for the local variable.]
> volatile unsigned char temp_v = (unsigned char) ui32;
cmos->uc = temp_v;
if ( cmos->uc != temp_v )
/* error writing to CMOS */
In article <87************@bsb.me.uk>
Ben Bacarisse <be********@bsb.me.ukwrote:
>Making temp_v volatile won't work.
Well, it *might* work, all depending on details about the
compiler's internal workings.
>The compiler may assume that cmos->uc is set as per the assignment
(it need not access the object again).
Right -- for instance, it might generate code of the form:

ldw cmos_, a3 # so that register a3 = cmos
ldb -12(sp), d1 # so that register d1 = temp_v
stb d1, 48(a3) /* cmos->uc = temp_v; */

ldb -12(sp), d2 # so that register d2 = temp_v
cmp d1, d2 /* see if cmos->uc == temp_v */
...

Note that temp_v was loaded twice, in case it changed; but the
compiler could see that 48(a3), which refers to cmos->uc, was set
from register d1, and -- since it is "ordinary RAM" (even though
it is not!) it must not have changed, so there was no need to load
*that* again.
>I can't see a way round this other than making the object that is
actually volatile, volatile.
Indeed.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 11 '07 #8

P: n/a
My apologies - I had meant to submit a thank-you message to all your
responses before now.

So, thanks for the helpful advice.

I have two related questions. According to K&R2:

"Except that it should diagnose explicit attempts to change 'const'
objects, a compiler may ignore these qualifiers."

"These qualifiers" are 'const' and 'volatile'. It seems that this could be
an issue. I can carefully implement Eric Sosman's suggestion regarding using
a volatile pointer to the structure, but it seems an ANSI/ISO conformant
compiler is free to ignore it, and the compiler then could optimise away the
following memcmp(). Could someone clarify this for me?

Also, along those lines of making a pointer volatile, I would like some
clarification. Consider this code abstract:

volatile char arr[10];
char arr2[10]
/* ... code that initialises both arrays ... */
if ( memcmp(&arr[3], &arr2[3], 1) ... )
/* etc. */

Does the deferencing of the first argument, arr, mean that memcmp is being
handed a non-volatile type (which is, I believe, the principle behind Chris
Torek's suggestion)?

--
Martin

Nov 20 '07 #9

P: n/a
"Martin" <martin.o_brien@[no-spam]which.netwrites:
I have two related questions. According to K&R2:

"Except that it should diagnose explicit attempts to change 'const'
objects, a compiler may ignore these qualifiers."
This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects in
read-only memory, and that it is not obligated to put volatile
objects in a special section of memory either.
--
Ben Pfaff
http://benpfaff.org
Nov 20 '07 #10

P: n/a
"Ben Pfaff" <bl*@cs.stanford.eduwrote in message
news:87************@blp.benpfaff.org...
"Martin" <martin.o_brien@[no-spam]which.netwrites:
>I have two related questions. According to K&R2:

"Except that it should diagnose explicit attempts to change 'const'
objects, a compiler may ignore these qualifiers."

This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects in
read-only memory, and that it is not obligated to put volatile
objects in a special section of memory either.
Are you saying that the quote from K&R2 above does not apply in these
instances?

int i, *const cpi = &i;
const int *pci;

--
Martin

Nov 20 '07 #11

P: n/a
"Martin" <martin.o_brien@[no-spam]which.netwrites:
"Ben Pfaff" <bl*@cs.stanford.eduwrote in message
news:87************@blp.benpfaff.org...
>"Martin" <martin.o_brien@[no-spam]which.netwrites:
>>I have two related questions. According to K&R2:

"Except that it should diagnose explicit attempts to change 'const'
objects, a compiler may ignore these qualifiers."

This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects in
read-only memory, and that it is not obligated to put volatile
objects in a special section of memory either.

Are you saying that the quote from K&R2 above does not apply in these
instances?

int i, *const cpi = &i;
cpi is a const pointer to a non-const int. The compiler is not
obligated to put cpi into a read-only section.
const int *pci;
pci is a non-const pointer to a const int. The compiler may not
put pci into a read-only section.
--
char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa6 7f6aaa,0xaa9aa9f6,0x11f6},*p
=b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
Nov 21 '07 #12

P: n/a
Ben Pfaff wrote:
"Martin" <martin.o_brien@[no-spam]which.netwrites:
>I have two related questions. According to K&R2:

"Except that it should diagnose explicit attempts to change
'const' objects, a compiler may ignore these qualifiers."

This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects
in read-only memory, and that it is not obligated to put
volatile objects in a special section of memory either.
The following was my attempt to test incrementing of a void*. I
think I can imagine situations where this ability would be useful
to bypass pointer incrementation. BTW, cc is shorthand for:
gcc -W -Wall -ansi -pedantic
and accesses gcc 3.2.1.

[1] c:\c\junk>cat junk.c
#include <stdio.h>

int main(void) {
void *p, *pb;
char by;

p = &by;

pb = p++;
if (pb == p) puts("++ uses sizeof void* == 0");
else puts("No luck here");
return 0;
}

[1] c:\c\junk>cc junk.c
junk.c: In function `main':
junk.c:9: warning: wrong type argument to increment

[1] c:\c\junk>a
No luck here

--
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>
Try the download section.

--
Posted via a free Usenet account from http://www.teranews.com

Nov 21 '07 #13

P: n/a
CBFalconer <cb********@yahoo.comwrites:
Ben Pfaff wrote:
>"Martin" <martin.o_brien@[no-spam]which.netwrites:
>>I have two related questions. According to K&R2:

"Except that it should diagnose explicit attempts to change
'const' objects, a compiler may ignore these qualifiers."

This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects
in read-only memory, and that it is not obligated to put
volatile objects in a special section of memory either.

The following was my attempt to test incrementing of a void*. I
think I can imagine situations where this ability would be useful
to bypass pointer incrementation. [...]
I am struggling to understand how this is anything but a non
sequitur. Can you explain?
--
Ben Pfaff
http://benpfaff.org
Nov 21 '07 #14

P: n/a

"Ben Pfaff" <bl*@cs.stanford.eduwrote in message
news:87************@blp.benpfaff.org...
CBFalconer <cb********@yahoo.comwrites:
>Ben Pfaff wrote:
>>"Martin" <martin.o_brien@[no-spam]which.netwrites:

I have two related questions. According to K&R2:

"Except that it should diagnose explicit attempts to change
'const' objects, a compiler may ignore these qualifiers."

This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects
in read-only memory, and that it is not obligated to put
volatile objects in a special section of memory either.

The following was my attempt to test incrementing of a void*. I
think I can imagine situations where this ability would be useful
to bypass pointer incrementation. [...]

I am struggling to understand how this is anything but a non
sequitur. Can you explain?
A void pointer has no stride and cannot be incremented. I find the sentence
very difficult to parse.

--
Posted via a free Usenet account from http://www.teranews.com

Nov 21 '07 #15

P: n/a
"Dann Corbit" <dc*****@connx.comwrites:
"Ben Pfaff" <bl*@cs.stanford.eduwrote in message
news:87************@blp.benpfaff.org...
>CBFalconer <cb********@yahoo.comwrites:
>>Ben Pfaff wrote:
"Martin" <martin.o_brien@[no-spam]which.netwrites:

I have two related questions. According to K&R2:
>
"Except that it should diagnose explicit attempts to change
'const' objects, a compiler may ignore these qualifiers."

This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects
in read-only memory, and that it is not obligated to put
volatile objects in a special section of memory either.

The following was my attempt to test incrementing of a void*. I
think I can imagine situations where this ability would be useful
to bypass pointer incrementation. [...]

I am struggling to understand how this is anything but a non
sequitur. Can you explain?

A void pointer has no stride and cannot be incremented. I find the sentence
very difficult to parse.
By "non sequitur", I mean that CBFalconer's reply seems to have
nothing to do with anything in the text that he quoted. Do you
see any connection?
--
Ben Pfaff
http://benpfaff.org
Nov 21 '07 #16

P: n/a
On Nov 21, 1:13 am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
You haven't changed anything as far as the constraint violation on the
call to memcmp is concerned. Both

volatile char x[10];
memcmp(x, ...);
memcmp(&x[3], ...);

and

volatile char *x;
memcmp(x, ...);
memcmp(&x[3], ...);

all pass a 'char pointer to volatile' where 'void pointer to const' is
expected.
OK, thanks for the response.

As has been my aim in this thread, I want to write code that will
verify that a write to CMOS has succeeded, and ensure that the
compiler does not optimise away the verification.

What follows is a solution, based on the previous comments in this
thread. As far as I can tell it looks OK. Obviously, if anyone on
c.l.c. thinks differently, I'd be grateful for the comments.

typedef struct {
...
somestruct s;
...
} CMOSTYPE;

CMOSTYPE cmos;

....

/* Create a volatile pointer to the cmos structure */
CMOSTYPE * volatile pcmos = &cmos;
somestruct s;

/* Assign some values to the members of s */

....

/* Copy s into the cmos structure */
memcpy(&pcmos->s, &s, sizeof(pcmos->s));

/* Check that the CMOS write succeeded */
if (memcmp(&pcmos->s, &s, sizeof(pcmos->s)) != 0)
/* Write failed */

--
Martin
Nov 21 '07 #17

P: n/a
I had meant to provide a rationale for the code in my previous
message, but somehow it got missed during the copy & paste process!
Here it is.

What if I were to use a volatile pointer to non-volatile data? I
think this would still prevent the compiler from optimising away the
comparison (because as far as the compiler is concerned the pointer
could change right up to the moment that the comparison is made).
This should also allow me to use memcmp to make comparisons because
instead of passing a pointer to volatile data I would be passing a
volatile pointer to the data.

--
Martin
Nov 21 '07 #18

P: n/a
Martin wrote:
On Nov 21, 1:13 am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
>You haven't changed anything as far as the constraint violation on the
call to memcmp is concerned. Both

volatile char x[10];
memcmp(x, ...);
memcmp(&x[3], ...);

and

volatile char *x;
memcmp(x, ...);
memcmp(&x[3], ...);

all pass a 'char pointer to volatile' where 'void pointer to const' is
expected.

OK, thanks for the response.

As has been my aim in this thread, I want to write code that will
verify that a write to CMOS has succeeded, and ensure that the
compiler does not optimise away the verification.

What follows is a solution, based on the previous comments in this
thread. As far as I can tell it looks OK. Obviously, if anyone on
c.l.c. thinks differently, I'd be grateful for the comments.

typedef struct {
...
somestruct s;
...
} CMOSTYPE;

CMOSTYPE cmos;

...

/* Create a volatile pointer to the cmos structure */
CMOSTYPE * volatile pcmos = &cmos;
somestruct s;

/* Assign some values to the members of s */

...

/* Copy s into the cmos structure */
memcpy(&pcmos->s, &s, sizeof(pcmos->s));

/* Check that the CMOS write succeeded */
if (memcmp(&pcmos->s, &s, sizeof(pcmos->s)) != 0)
/* Write failed */
You have been told, several times and by several people,
that it is an error to pass a pointer to volatile data to a
function that isn't aware of that volatility. What have you
done with this carefully-explained information? You ignore
it, that's what, and you pass pointers to volatile data to
memcpy() and memcmp(), functions unaware of the volatility.

I give up.

--
Eric Sosman
es*****@ieee-dot-org.invalid
Nov 21 '07 #19

P: n/a
On Nov 21, 1:30 pm, Eric Sosman <esos...@ieee-dot-org.invalidwrote:
You have been told, several times and by several people,
that it is an error to pass a pointer to volatile data to a
function that isn't aware of that volatility. What have you
done with this carefully-explained information? You ignore
it, that's what, and you pass pointers to volatile data to
memcpy() and memcmp(), functions unaware of the volatility.

I give up.
Don't give up, the advice is appreciated. If I *appear* to be ignoring
the advice it's because there is something I am not understanding.

As I explained in my last post, I am passing a volatile pointer to non-
volatile data, I am not passing a pointer to volatile data; therefore,
the data dereferenced by both memcmp() and memcpy() are not volatile.
I think the distinction is important and I explained my rationale for
doing so in the message. So why is it still wrong?

--
Martin
Nov 21 '07 #20

P: n/a
Ben Pfaff wrote:
CBFalconer <cb********@yahoo.comwrites:
>Ben Pfaff wrote:
.... snip ...
>>
>>This statement is given in the context of qualifiers on types,
not qualifiers on pointers. I think that this is intended to
mean that the compiler is not obligated to store const objects
in read-only memory, and that it is not obligated to put
volatile objects in a special section of memory either.

The following was my attempt to test incrementing of a void*. I
think I can imagine situations where this ability would be useful
to bypass pointer incrementation. [...]

I am struggling to understand how this is anything but a non
sequitur. Can you explain?
I have to admit I am confused myself. :=) As I vaguely recall, I
had some ideas about exploring some crevice in your statement, but
after the fact I can't see it. However, my sample (that you
snipped) stands, and should maybe be considered.

--
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>
Try the download section.

--
Posted via a free Usenet account from http://www.teranews.com

Nov 21 '07 #21

P: n/a
Martin wrote:
On Nov 21, 1:30 pm, Eric Sosman <esos...@ieee-dot-org.invalidwrote:
> You have been told, several times and by several people,
that it is an error to pass a pointer to volatile data to a
function that isn't aware of that volatility. What have you
done with this carefully-explained information? You ignore
it, that's what, and you pass pointers to volatile data to
memcpy() and memcmp(), functions unaware of the volatility.

I give up.

Don't give up, the advice is appreciated. If I *appear* to be ignoring
the advice it's because there is something I am not understanding.

As I explained in my last post, I am passing a volatile pointer to non-
volatile data, I am not passing a pointer to volatile data; therefore,
the data dereferenced by both memcmp() and memcpy() are not volatile.
I think the distinction is important and I explained my rationale for
doing so in the message. So why is it still wrong?
If that's what you think you're doing, your declaration is incorrect. Change
volatile char *parr = arr;

into

char * volatile parr = arr;
Nov 21 '07 #22

P: n/a
On Nov 21, 3:37 pm, James Kuyper <jameskuy...@verizon.netwrote:
If that's what you think you're doing, your declaration is incorrect. Change

volatile char *parr = arr;

into

char * volatile parr = arr;
But that's exactly what I've done, albeit in the form:

. . .
CMOSTYPE * volatile pcmos = &cmos;
. . .

--
Martin
Nov 21 '07 #23

P: n/a
CBFalconer <cb********@yahoo.comwrites:
The following was my attempt to test incrementing of a void*. I
think I can imagine situations where this ability would be useful
to bypass pointer incrementation. BTW, cc is shorthand for:
gcc -W -Wall -ansi -pedantic
and accesses gcc 3.2.1.

[1] c:\c\junk>cat junk.c
#include <stdio.h>

int main(void) {
void *p, *pb;
char by;

p = &by;

pb = p++;
if (pb == p) puts("++ uses sizeof void* == 0");
else puts("No luck here");
return 0;
}
I'm not sure what you're hoping to demonstrate here. The size of
an incomplete type cannot be taken in ISO C, and GCC with
-pedantic amply demonstrates that by giving the required
diagnostic. That GCC then allows the program to compile to an
executable is not really a kindness.

The situation when GCC is run without -pedantic is a little
different, but GCC is not an ISO C compiler in that case.
--
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv wxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p[i]\
);}return 0;}
Nov 21 '07 #24

P: n/a
Martin wrote:
On Nov 21, 3:37 pm, James Kuyper <jameskuy...@verizon.netwrote:
>If that's what you think you're doing, your declaration is incorrect. Change

volatile char *parr = arr;

into

char * volatile parr = arr;

But that's exactly what I've done, albeit in the form:

. . .
CMOSTYPE * volatile pcmos = &cmos;
I searched back to the message you sent with the header:

Date: Tue, 20 Nov 2007 22:41:27 -0000

In that message, you declared parr exactly as indicated above. For some
reason I skipped over the one with the header

Date: Wed, 21 Nov 2007 03:27:10 -0800 (PST)

With that declaration, the problem is indeed fixed. pcmos->s is not a
volatile object, and therefore doesn't violate 6.7.3p5. For precisely
the same reason, it doesn't achieve your goal of preventing a compiler
from optimizing away the memcmp() call.
Nov 21 '07 #25

P: n/a
On Nov 21, 4:05 pm, James Kuyper <jameskuy...@verizon.netwrote:
With that declaration, the problem is indeed fixed. pcmos->s is not a
volatile object, and therefore doesn't violate 6.7.3p5. For precisely
the same reason, it doesn't achieve your goal of preventing a compiler
from optimizing away the memcmp() call.
Thanks for your response.

Surely the fact that pcmos is now a volatile pointer prevents the
compiler from optimising away the memcmp because the compiler won't
know at compile-time that pcmos won't change between the memcpy and
the memcmp?

--
Martin
Nov 21 '07 #26

P: n/a
Martin <ma************@which.netwrites:
On Nov 21, 1:13 am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
>You haven't changed anything as far as the constraint violation on the
call to memcmp is concerned.
<snip>
>
OK, thanks for the response.
<snip>
What follows is a solution, based on the previous comments in this
thread. As far as I can tell it looks OK. Obviously, if anyone on
c.l.c. thinks differently, I'd be grateful for the comments.

typedef struct {
...
somestruct s;
...
} CMOSTYPE;

CMOSTYPE cmos;

...

/* Create a volatile pointer to the cmos structure */
CMOSTYPE * volatile pcmos = &cmos;
somestruct s;

/* Assign some values to the members of s */

...

/* Copy s into the cmos structure */
memcpy(&pcmos->s, &s, sizeof(pcmos->s));

/* Check that the CMOS write succeeded */
if (memcmp(&pcmos->s, &s, sizeof(pcmos->s)) != 0)
/* Write failed */
You have just switched the volatile property to something else. The
pointer is now volatile (probably not true) so that call is now OK but
can be optimised away is the compiler sees fit. There is no option
but to mark the thing that is volatile as volatile. If you can't do
that for some reason then I can't see a solution.

--
Ben.
Nov 21 '07 #27

P: n/a
On Nov 21, 4:25 pm, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
You have just switched the volatile property to something else. The
pointer is now volatile (probably not true) so that call is now OK but
can be optimised away is the compiler sees fit. There is no option
but to mark the thing that is volatile as volatile. If you can't do
that for some reason then I can't see a solution.

Thanks for the response Ben.

Your solution (of making the CMOS structure volatile) will only work
(as others here have pointed out) in the case where the item I am
writing and verifying in the cmos structure can be compared with a
simple ==. Where I write a structure such as a date/time (using
memcpy) I need to use memcmp to check that it wrote properly and I
cannot do so if the target is volatile (or if I am pretending it is).

--
Martin
Nov 21 '07 #28

P: n/a
In article
<be**********************************@d61g2000hsa. googlegroups.com>,
Martin <ma************@which.netwrote on Wednesday 21 Nov 2007 10:55
pm:
On Nov 21, 4:25 pm, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
>You have just switched the volatile property to something else. The
pointer is now volatile (probably not true) so that call is now OK
but
can be optimised away is the compiler sees fit. There is no option
but to mark the thing that is volatile as volatile. If you can't do
that for some reason then I can't see a solution.


Thanks for the response Ben.

Your solution (of making the CMOS structure volatile) will only work
(as others here have pointed out) in the case where the item I am
writing and verifying in the cmos structure can be compared with a
simple ==. Where I write a structure such as a date/time (using
memcpy) I need to use memcmp to check that it wrote properly and I
cannot do so if the target is volatile (or if I am pretending it is).
Compare member by member individually.

Nov 21 '07 #29

P: n/a
In article <be**********************************@d61g2000hsa. googlegroups.com>
Martin <ma************@which.netwrote:
>(... making the CMOS structure volatile) will only work
(as others here have pointed out) in the case where the item I am
writing and verifying in the cmos structure can be compared with a
simple ==. Where I write a structure such as a date/time (using
memcpy) I need to use memcmp to check that it wrote properly ...
Using memcmp() is rather likely to fail anyway, depending on
the target architecture.

Suppose memcmp() is a typical assembly-language "optimized" version,
that checks the alignment of its incoming pointers and the sizes
of the regions to compare. If the size is large enough (at least
4 or 8 bytes) and the addresses are properly aligned, it does, in
effect, the assembly equivalent of the following C code:

unsigned char *left, *right; /* then initialized from parameters */
size_t len; /* initialized from parameter */

if (properly_aligned) {
while (len >= sizeof(int) && *(int *)left == *(int *)right)
left += sizeof(int), right += sizeof(int), len -= sizeof(int);
}
while (len) {
if (*left != *right)
return *left < *right ? -1 : 1;
left++, right++, len--;
}
return 0;

Now, on a lot of hardware, *(unsigned char *)addr does a 1-byte
read on the bus, which is what is required to access an EEPROM
device; but *(int *)addr does a 4-byte read on the bus. If a 4-byte
read reaches the EEPROM ("CMOS") hardware, an incorrect value is
read from the device, or an exception ("bus error" or similar) is
delivered to the CPU.

Not all hardware works like this[%], and sometimes memcmp() really
can be used directly against "hardware memory", but exceptions
are common enough (in my experience anyway) to assume the worst,
and avoid memcmp() here.

[% Two particularly bizarre examples occur with 8-bit hardware
attached to "32-bit-only" CPUs. Here, "load byte" does a 32-bit
bus access, or there is no separate "load byte" instruction, so
the hardware designer takes one of two approaches. Either every
8-bit byte is placed on a 32-bit boundary, so that *(int *)addr is
0x000000NN or 0xNN000000 depending on endian-ness -- and sometimes
the 00s are FFs instead, or are duplicates of the NN byte; or,
instead, the 32-bit access is put on hold while the adapter does
four 8-bit accesses so as to "construct" the full 32 bit value,
which is then placed on the data bus. In this second case, it
really *can* be good idea to use 32-bit operations to access the
device -- typically to copy the entire ROM contents to RAM for
faster reference.]
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 21 '07 #30

P: n/a
Martin wrote:
) Thanks for the response Ben.
)
) Your solution (of making the CMOS structure volatile) will only work
) (as others here have pointed out) in the case where the item I am
) writing and verifying in the cmos structure can be compared with a
) simple ==. Where I write a structure such as a date/time (using
) memcpy) I need to use memcmp to check that it wrote properly and I
) cannot do so if the target is volatile (or if I am pretending it is).

Well then the solution is easy:
Write your own memcmp using volatile pointers.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
Nov 21 '07 #31

P: n/a
Martin wrote:
On Nov 21, 4:05 pm, James Kuyper <jameskuy...@verizon.netwrote:
>With that declaration, the problem is indeed fixed. pcmos->s is not a
volatile object, and therefore doesn't violate 6.7.3p5. For precisely
the same reason, it doesn't achieve your goal of preventing a compiler
from optimizing away the memcmp() call.

Thanks for your response.

Surely the fact that pcmos is now a volatile pointer prevents the
compiler from optimising away the memcmp because the compiler won't
know at compile-time that pcmos won't change between the memcpy and
the memcmp?
Not really. if pcmos is subject to random changes between the calls to
memcpy() and memcmp(), it might no longer point at the same object it
was originally set to; it might even contain a null pointer or a trap
representation. Therefore, the call to memcmp() will have undefined
behavior. That includes the possibility of optimizing away the call to
memcmp().

I really don't understand why you want to prevent this optimization. It
is permitted only when it will make no difference, in which case there's
no point to calling memcmp() (and also no point to preventing the
optimization), or when the behavior is undefined - and there's no point
to deliberately writing code unless there's something that gives the
code defined behavior.
Nov 21 '07 #32

P: n/a
Ben Pfaff wrote:
CBFalconer <cb********@yahoo.comwrites:
>The following was my attempt to test incrementing of a void*. I
think I can imagine situations where this ability would be useful
to bypass pointer incrementation. BTW, cc is shorthand for:
gcc -W -Wall -ansi -pedantic
and accesses gcc 3.2.1.

[1] c:\c\junk>cat junk.c
#include <stdio.h>

int main(void) {
void *p, *pb;
char by;

p = &by;

pb = p++;
if (pb == p) puts("++ uses sizeof void* == 0");
else puts("No luck here");
return 0;
}

I'm not sure what you're hoping to demonstrate here. The size of
an incomplete type cannot be taken in ISO C, and GCC with
-pedantic amply demonstrates that by giving the required
diagnostic. That GCC then allows the program to compile to an
executable is not really a kindness.

The situation when GCC is run without -pedantic is a little
different, but GCC is not an ISO C compiler in that case.
The situation I was trying to control was ridiculous anyhow.
Basically I wanted to see if the increment of a void* became a NOP,
so control of the type would enable it. Possibly it would make
more sense to have a void* point to something of size zero, but I
don't know offhand what other effects that would have. At any rate
it doesn't deserve any further consideration.

I must be getting older. I am proposing more foolish things these
days.

--
Chuck F (cbfalconer at maineline dot net)
<http://cbfalconer.home.att.net>
Try the download section.

--
Posted via a free Usenet account from http://www.teranews.com

Nov 22 '07 #33

P: n/a
On Nov 21, 6:04 pm, Chris Torek <nos...@torek.netwrote:
[...]
Not all hardware works like this[%], and sometimes memcmp() really
can be used directly against "hardware memory", but exceptions
are common enough (in my experience anyway) to assume the worst,
and avoid memcmp() here.
Thanks for your detailed response, Chris.

I have checked the implementation of memcmp (and strcmp) in the
library we're using and both are done byte by byte so while the use of
memcmp on CMOS may not be totally portable it should at least be safe
in our current architecture.

--
Martin
Nov 22 '07 #34

P: n/a
James wrote:
) Let me say this frankly: what you say you want to do is pointless, for
) precisely the same reason that you can't prevent the optimization. If
) there's really a point to what you want to do, you haven't made that
) clear. You need to expand your description of the problem to make that
) point clear; as soon as you do so, we can tell you what to do to prevent
) the optimization.

IIRC, What he wants to do is write to a specific bit of memory, and then
check if the write was OK by reading back the data and comparing.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
Nov 22 '07 #35

P: n/a
Willem wrote:
James wrote:
) Let me say this frankly: what you say you want to do is pointless, for
) precisely the same reason that you can't prevent the optimization. If
) there's really a point to what you want to do, you haven't made that
) clear. You need to expand your description of the problem to make that
) point clear; as soon as you do so, we can tell you what to do to prevent
) the optimization.

IIRC, What he wants to do is write to a specific bit of memory, and then
check if the write was OK by reading back the data and comparing.
Since a correct implementation of C guarantees that the comparison
results in an exact match, it can be optimized away. That's also why the
comparison is pointless. As I explained before, the best you can do, if
you suspect a compiler of implementing either memcpy() or memcmp()
incorrectly, is to select a compiler mode that turns off such optimizations.

If that isn't an option, the next-best choice is to perform the copy and
the comparison in two different translation units, which makes it much
harder (but not impossible) to perform such optimizations. The
optimization would have to be done by a smart linker, rather than in the
compiler itself. Linkers with that much intelligence are supposedly
state of the art for C++. With modern implementations of C often
piggy-backed on C++ implementations, I wouldn't rule out the possibility
of optimizations that can only be performed during the linking process.
Nov 22 '07 #36

P: n/a

"James Kuyper" <ja*********@verizon.netwrote:
Let me say this frankly: what you say you want to do is pointless, for
precisely the same reason that you can't prevent the optimization. If
there's really a point to what you want to do, you haven't made that
clear. You need to expand your description of the problem to make that
point clear; as soon as you do so, we can tell you what to do to prevent
the optimization.
What I want to do is far from pointless. When I wrote "I want to write code
that will verify that a write to CMOS has succeeded, and ensure that the
compiler does not optimise away the verification" that's exactly what I
wanted to do.

The detail is simply that the code is writing to CMOS (hence the variable
names derived from it). CMOS is an area that has a limited number of writes
available to it (I assumed this was fairly common knowledge). After a
certain amount of use, writes can, do, and have been failing. The code has
not been detecting that and now we've decided it must. We need to tell the
compiler that it mustn't assume that a write to CMOS has succeeded (using
memcpy or whatever) - it *must* perform the verification, using memcmp, or
whatever. It's basically a memory test.

The only reason I can think of for your comments is that you were assuming I
wanted to check the write because I wasn't confident that memcpy was
implemented correctly. As I have now explained, it's because the CMOS may be
failing.

Thanks for your time.

--
Martin

Nov 22 '07 #37

P: n/a
Martin wrote:
"James Kuyper" <ja*********@verizon.netwrote:
>Let me say this frankly: what you say you want to do is pointless, for
precisely the same reason that you can't prevent the optimization. If
there's really a point to what you want to do, you haven't made that
clear. You need to expand your description of the problem to make that
point clear; as soon as you do so, we can tell you what to do to prevent
the optimization.

What I want to do is far from pointless. When I wrote "I want to write code
that will verify that a write to CMOS has succeeded, and ensure that the
compiler does not optimise away the verification" that's exactly what I
wanted to do.

The detail is simply that the code is writing to CMOS (hence the variable
names derived from it). CMOS is an area that has a limited number of writes
available to it (I assumed this was fairly common knowledge). After a
certain amount of use, writes can, do, and have been failing. The code has
not been detecting that and now we've decided it must. We need to tell the
compiler that it mustn't assume that a write to CMOS has succeeded (using
memcpy or whatever) - it *must* perform the verification, using memcmp, or
whatever. It's basically a memory test.

The only reason I can think of for your comments is that you were assuming I
wanted to check the write because I wasn't confident that memcpy was
implemented correctly. As I have now explained, it's because the CMOS may be
failing.
You said that the memory being pointed at was not volatile. My comments
were predicated on taking you at your word. Now you've described that
memory in a fashion that has to be described as volatile.

The best solution you can use in that case has already been mentioned:
instead of using memcmp(), write your own version that takes pointers to
volatile as arguments. It's pretty trivial to do. The reason the
standard memcmp() is not written that way is that in general its output
would be pretty meaningless: since the pointed-at memory is volatile,
the only thing the return value from volatile_memcmp() would tell you is
how the bytes compared at the times that they were compared. Inherently,
different bytes are necessarily compared at different times, so it
cannot even be considered a snapshot comparison.

However, if you assume that the memory you're looking at is changing
sufficiently slowly, volatile_memcmp() could be a useful and meaningful
thing to define.
Nov 22 '07 #38

P: n/a
In article <nG*****************@fe79.usenetserver.com>, Martin
<martin.o_brien@[no-spam]which.netwrote on Friday 23 Nov 2007 4:16
am:
>
"James Kuyper" <ja*********@verizon.netwrote:
>Let me say this frankly: what you say you want to do is pointless,
for precisely the same reason that you can't prevent the
optimization. If there's really a point to what you want to do, you
haven't made that clear. You need to expand your description of the
problem to make that point clear; as soon as you do so, we can tell
you what to do to prevent the optimization.

What I want to do is far from pointless. When I wrote "I want to write
code that will verify that a write to CMOS has succeeded, and ensure
that the compiler does not optimise away the verification" that's
exactly what I wanted to do.
Instead of this ongoing headbanging, why don't you just use a small
assembler routine for this purpose and be done with it? Then no
compiler can get it's grubby hands on your code.

Writing to CMOS is non-portable anyway, so a machine specific routine
should not be too much of a bother.
The detail is simply that the code is writing to CMOS (hence the
variable names derived from it). CMOS is an area that has a limited
number of writes available to it (I assumed this was fairly common
knowledge). After a certain amount of use, writes can, do, and have
been failing. The code has not been detecting that and now we've
decided it must. We need to tell the compiler that it mustn't assume
that a write to CMOS has succeeded (using memcpy or whatever) - it
*must* perform the verification, using memcmp, or whatever. It's
basically a memory test.
The best tool for this sort of job, which requires fine control and the
complete absence of any possible optimisation, is assembler.

<snip>

Nov 23 '07 #39

P: n/a
"Martin" <martin.o_brien@[no-spam]which.neta écrit dans le message de
news: nG*****************@fe79.usenetserver.com...
>
"James Kuyper" <ja*********@verizon.netwrote:
>Let me say this frankly: what you say you want to do is pointless, for
precisely the same reason that you can't prevent the optimization. If
there's really a point to what you want to do, you haven't made that
clear. You need to expand your description of the problem to make that
point clear; as soon as you do so, we can tell you what to do to prevent
the optimization.

What I want to do is far from pointless. When I wrote "I want to write
code that will verify that a write to CMOS has succeeded, and ensure that
the compiler does not optimise away the verification" that's exactly what
I wanted to do.

The detail is simply that the code is writing to CMOS (hence the variable
names derived from it). CMOS is an area that has a limited number of
writes available to it (I assumed this was fairly common knowledge). After
a certain amount of use, writes can, do, and have been failing. The code
has not been detecting that and now we've decided it must. We need to tell
the compiler that it mustn't assume that a write to CMOS has succeeded
(using memcpy or whatever) - it *must* perform the verification, using
memcmp, or whatever. It's basically a memory test.

The only reason I can think of for your comments is that you were assuming
I wanted to check the write because I wasn't confident that memcpy was
implemented correctly. As I have now explained, it's because the CMOS may
be failing.
The volatile keyword is what you need. Qualifying this "unreliable" as
volatile will require that all accesses be coded and not optimized away. Why
don't you write a small loop where you read back each byte that you copy,
and try again a few times upon failure before giving up with an error code.

--
Chqrlie.
Nov 23 '07 #40

This discussion thread is closed

Replies have been disabled for this discussion.