Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ARM/Linux: Is this a cross-compiler bug? (memcpy doesn't work as expected)

181 views
Skip to first unread message

Steven Woody

unread,
Jul 22, 2008, 2:01:42 AM7/22/08
to
Hi,

I tried two different Linux/ARM cross-compiler on the following code
( see the last part of this message), one is from ELDK (http://
www.denx.de/wiki/DULG/WebHome), another is from our board vendor,
both result a same error.

Below is the running output on an ARM920T board:

before: p1:
0xfc 0x01 0x12
before: p2:
0x40 0x19 0x21
after: p1:
0x40 0x01 0x02
after: p2:
0x40 0x19 0x21
!!! cp is wrong

That is, the line 38 which copy 3 bytes, starting from p2, to p1, but
the immediately followed memcmp(p1, p2, 3) failed. I am sure the
memcpy did not do the job, since if I provide my own memcpy
implemention as below, the error will go disappear.

void memcpy(void *dest, void *src, size_t n)
{
uint8_t *d = (uint8_t*)dest;
uint8_t *s = (uint8_t*)src;

for (size_t i = 0; i < n; ++i)
*d++ = *s++;
}


Another way to make the error going away is not to use any
optimization when compile. My test case use -O2 to bring up the
error, but If I don't use it, the error won't occur.

I can not understand what happened here, the result shows, as you see,
my assignment of p2 (line 34) is okay because the print output of line
36 is correct. And, if that is a bug in runtime c library -- remember
my own memcpy works -- why the two cross compiler coming from
different sources result in the same error?

------------------------------------- the minimum sample
--------------------------------------------------
1 #include <stdio.h>
2 #include <string>
3 #include <stdint.h>
4 #include <assert.h>
5
6 struct Foo {
7 uint8_t x;
8 uint8_t y;
9 uint8_t z;
10 uint8_t m[3];
11 };
12
13 struct Bar
14 {
15 uint8_t m[3];
16 };
17
18 void pr(const char *title, const void *block, size_t n)
19 {
20 printf("%s\n", title);
21
22 uint8_t *p = (uint8_t*)block;
23 for (size_t i = 0; i < n; ++i)
24 printf("0x%02x ", *p++);
25
26 printf("\n");
27 }
28
29 void cp(const Foo *foo)
30 {
31 Bar bar;
32
33 Bar *p1 = &bar;
34 Bar *p2 = (Bar*)(foo->m);
35 pr("before: p1:", p1, 3);
36 pr("before: p2:", p2, 3);
37
38 memcpy(p1, p2, 3);
39 pr("after: p1:", p1, 3);
40 pr("after: p2:", p2, 3);
41
42 if (memcmp(p1, p2, 3) != 0)
43 printf("!!! cp is wrong\n");
44 }
45
46 int main()
47 {
48 Foo foo;
49 foo.x = 1;
50 foo.y = 2;
51 foo.z = 3;
52 foo.m[0] = 0x40;
53 foo.m[1] = 0x19;
54 foo.m[2] = 0x21;
55
56 cp(&foo);
57 cp2(&foo);
58 return 0;
59 }
---------------------------------------------------------------------------------------

Frank Buss

unread,
Jul 22, 2008, 3:10:07 AM7/22/08
to
Steven Woody wrote:

> 33 Bar *p1 = &bar;
> 34 Bar *p2 = (Bar*)(foo->m);

I can reproduce the problem. But if I write this:

void *p1 = &bar;
void *p2 = (void*)(foo->m);

it works, at least with g++ 4.1.2 on my NAS system, for which I've changed
the internal firmware to a standard Debian Linux. The interesting thing: If
I'm using my own memcpy, as you described:

void mymemcpy(void* p1, void* p2, int size)
{
unsigned char* p11 = (unsigned char*) p1;
unsigned char* p22 = (unsigned char*) p2;
for (int i = 0; i < size; i++) p11[i] = p22[i];
}

it works all the time. I guess this could be an alignment problem with
memcpy. You should file a bug report, see http://gcc.gnu.org/bugs.html for
instructions. You can add my report.

Compile command: g++ -O2 -Wall test.c

System on which I've tested it:

# g++ --version
g++ (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)

# cat /proc/cpuinfo
Processor : ARM926EJ-Sid(wb) rev 0 (v5l)
BogoMIPS : 266.24
Features : swp half thumb fastmult edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant : 0x0
CPU part : 0x926
CPU revision : 0

--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de

Frank Buss

unread,
Jul 22, 2008, 3:56:26 AM7/22/08
to
Steven Woody wrote:

> I tried two different Linux/ARM cross-compiler on the following code
> ( see the last part of this message), one is from ELDK (http://
> www.denx.de/wiki/DULG/WebHome), another is from our board vendor,
> both result a same error.

I guess most C ARM cross compilers are based on the GNU compiler, which
could be the reason why it is the same error. There are other bugs for the
ARM implementation, which I could confirm and was fixed:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31152

Maybe try the latest version of GCC, if you are lucky and manage to compile
the compiler :-)

If I compile the code with Visual Studio 2005 for ARM (for a WindowsCE
system), your code works, too (needs only one additional "typedef unsigned
char uint8_t", because the compiler is not fully ANSI C99 compliant and
doesn't know the header file stdint.h).

Nils

unread,
Jul 22, 2008, 4:21:52 PM7/22/08
to
This is not strictly a bug..

You're violating the C-rules of aliasing. In short: You must not modify
a value/stucture of type A and read from the same memory by casting a
pointer from A to B. The compiler is allowed to assume that because the
types differ the two pointers point to different memory.


I know this is common practice. However, the C-compiler can do anything
it wants to in this case. Gcc got a better aliasing analysis backend
recently that is more strict about the rules. Lots of old code that
depended on this behaviour broke, but even more code improved in
performance.. All in all that was a good change.

The way around this is to cast the one of the pointers temporarily to
(char *). That's the official way to tell a C-compiler that the data may
alias with other data. Char * is the wild-card. Another - more drastic
way would be to declare the pointer you read from as volatile, but
that effectively disables any optimizations.


FYI: Delacring the pointers as void* may help as well. Also: x86 code
often works simply due to the fact that values have to be reloaded from
memory due to the lack of registers. The compiler could optimize bit it
can't because 7 registers aren't enough... So this is one of the bugs
you'll first encounter (if ever) if you compile your code on ARM or MIPS
or other architectures that have plenty of registers..

Nils

Wilco Dijkstra

unread,
Jul 22, 2008, 5:34:22 PM7/22/08
to

"Nils" <n.pipe...@cubic.org> wrote in message news:48864160...@cubic.org...

> This is not strictly a bug..
>
> You're violating the C-rules of aliasing. In short: You must not modify a value/stucture of type A and read from the
> same memory by casting a pointer from A to B. The compiler is allowed to assume that because the types differ the two
> pointers point to different memory.

There is no aliasing issue in the example. You would be right if he accessed
both foo and p2 at the same time in a context where the obvious relation
between the pointers is not visible (eg. if both are passed to a different
function). There is no aliasing, and neither the memcpy nor the initialization
of Foo is redundant (as its address is leaked), so it's a compiler bug.

Wilco


Frank Buss

unread,
Jul 23, 2008, 2:24:12 AM7/23/08
to
Nils wrote:

> This is not strictly a bug..
>
> You're violating the C-rules of aliasing. In short: You must not modify
> a value/stucture of type A and read from the same memory by casting a
> pointer from A to B. The compiler is allowed to assume that because the
> types differ the two pointers point to different memory.

Can you cite the chapters in the standard? My first feeling was the same,
because using void* worked, but it works with Visual C for ARM, which I
think does a good register optimization, too, and Wilco says, it is not an
aliasing problem.

JSprocket

unread,
Jul 23, 2008, 3:13:37 AM7/23/08
to
Nils wrote:

> I Gcc got a better aliasing analysis backend

> recently that is more strict about the rules. Lots of old code that

> depended on this behaviour broke...

In what sense is that "better"? A warning, perhaps, a switch for
"strict" mode, but breaking existing code is a stupid thing to do.

JS

Frank Buss

unread,
Jul 23, 2008, 3:52:37 AM7/23/08
to
Steven Woody wrote:

> That is, the line 38 which copy 3 bytes, starting from p2, to p1, but
> the immediately followed memcmp(p1, p2, 3) failed. I am sure the
> memcpy did not do the job, since if I provide my own memcpy
> implemention as below, the error will go disappear.

Looks like it is a problem with memcpy:

http://www.arm.com/support/faqdev/4972.html
http://gcc.gnu.org/ml/gcc-bugs/2001-12/msg00534.html

but I'm not sure, if it is a compiler bug. If you specify "-Ono_memcpy" or
"-fpack-struct" when compiling your code, there is no bug anymore. I guess
the casting of a member of a struct to a struct pointer is dangerous in
combination with the built-in memcpy functions.

Wilco Dijkstra

unread,
Jul 23, 2008, 5:27:00 AM7/23/08
to

"Frank Buss" <f...@frank-buss.de> wrote in message news:6by8aaa20pcp$.fxdf29mhrkt.dlg@40tude.net...

> Nils wrote:
>
>> This is not strictly a bug..
>>
>> You're violating the C-rules of aliasing. In short: You must not modify
>> a value/stucture of type A and read from the same memory by casting a
>> pointer from A to B. The compiler is allowed to assume that because the
>> types differ the two pointers point to different memory.
>
> Can you cite the chapters in the standard? My first feeling was the same,
> because using void* worked, but it works with Visual C for ARM, which I
> think does a good register optimization, too, and Wilco says, it is not an
> aliasing problem.

What the standard says is not relevant. The C standards are consistently
and deliberately underspecified, unclear and are more concerned with
features that were obsolete 20 years ago (like ones-complement and
signed overflow) than actually specifying important features like bitfields.
So compiler writers typically add their own rules, the most important of
which says "thou shalt correctly compile existing code".

What would be more interesting is the generated code for the cp()
function. That would clear up whether it is a bug in memcpy or inter
procedural alias analysis.

Btw. The -Ono_memcpy option you mentioned in another post only
works on the ARM compiler, which is not based on GCC.

Wilco


John Devereux

unread,
Jul 23, 2008, 7:48:19 AM7/23/08
to
JSprocket <J...@internept.org> writes:

If that were always true, compilers could never improve. Breakage of
*incorrectly* written code is quite common. For example non-volatile
accesses to hardware, or "Optimising" of delay loops and time
sensitive code.

--

John Devereux

Wilco Dijkstra

unread,
Jul 23, 2008, 8:49:22 AM7/23/08
to

"John Devereux" <jdRE...@THISdevereux.me.uk> wrote in message news:87od4od...@cordelia.devereux.me.uk...

Most compiler optimizations are independent of language semantics,
so it's not true compilers could not improve if they had to be conservative.
I know for a fact that one can beat compilers with aggressive optimizations
(like GCC) by a huge margin while being very conservative.

Of course if you don't use volatile correctly then it's your own fault.
Non-conforming code is not the same though. In general incorrect code
fails on most compilers when optimizations are enabled, while
non-conforming works on most compilers.

Wilco



CBFalconer

unread,
Jul 23, 2008, 8:48:21 AM7/23/08
to

The standard hasn't changed. Thus 'better' in the sense that code
that violates the standard is detected. The code didn't break, it
was always broken.

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


CBFalconer

unread,
Jul 23, 2008, 8:43:48 AM7/23/08
to
Wilco Dijkstra wrote:

> "Frank Buss" <f...@frank-buss.de> wrote:
>> Nils wrote:
>>
>>> This is not strictly a bug..
>>>
>>> You're violating the C-rules of aliasing. In short: You must
>>> not modify a value/stucture of type A and read from the same
>>> memory by casting a pointer from A to B. The compiler is
>>> allowed to assume that because the types differ the two
>>> pointers point to different memory.
>>
>> Can you cite the chapters in the standard? My first feeling was
>> the same, because using void* worked, but it works with Visual
>> C for ARM, which I think does a good register optimization, too,
>> and Wilco says, it is not an aliasing problem.
>
> What the standard says is not relevant. The C standards are
> consistently and deliberately underspecified, unclear and are
> more concerned with features that were obsolete 20 years ago
> (like ones-complement and signed overflow) than actually
> specifying important features like bitfields. So compiler
> writers typically add their own rules, the most important of
> which says "thou shalt correctly compile existing code".

What a horrible (and dangerous) attitude. Not only that, but
inaccurate also.

One of the great problems with C is the failure to catch overflow
in signed integral expressions. It is not solvable, because of the
necessity of supporting older code. Bitfields are unimportant,
because there are adequate replacements available in the form of
constants and bit manipulation operations.

One of the strengths of C is the fact that code that meets the
requirements of the standard is almost always totally portable. By
itself this is not the final word, but it makes it easy to isolate
and emphasize what code needs modification during a port.

Frank Buss

unread,
Jul 23, 2008, 10:03:09 AM7/23/08
to
CBFalconer wrote:

> One of the strengths of C is the fact that code that meets the
> requirements of the standard is almost always totally portable. By
> itself this is not the final word, but it makes it easy to isolate
> and emphasize what code needs modification during a port.

But the code of the OP demonstrates that sometimes it is difficult to meet
the standard. For me it is not clear, if the code is violating the
standard. Would be nice, if a compiled source code would behave exactly the
same on every platform and for all optimization switches, otherwise the
compiler should signal an error for ambiguities.

Is there any other standarized computer language, which is more restrictive
defined? Like Java: There are no dangerous pointer operations, alignment
and aliasing problems, the size of the basic types are fixed for every
platform, the VM is simple and well defined etc. But Java is not a
standard, e.g. like the ECMAScript standard (aka JavaScript). Looks like C#
and the upcoming C++/CLI are interesting languages, but because of the CLI
it would be difficult to use it for small microcontrollers.

Is it possible to define it more restrictive and still fast at all? If
"int" would be defined 32 bit for all platforms, but the microcontroller
has only 16 bit native registers, this would be slower than a C
implementation.

Walter Banks

unread,
Jul 23, 2008, 10:04:29 AM7/23/08
to

Wilco Dijkstra wrote:

>
> > If that were always true, compilers could never improve. Breakage of
> > *incorrectly* written code is quite common. For example non-volatile
> > accesses to hardware, or "Optimising" of delay loops and time
> > sensitive code.
>
> Most compiler optimizations are independent of language semantics,
> so it's not true compilers could not improve if they had to be conservative.
> I know for a fact that one can beat compilers with aggressive optimizations
> (like GCC) by a huge margin while being very conservative.
>
> Of course if you don't use volatile correctly then it's your own fault.
> Non-conforming code is not the same though. In general incorrect code
> fails on most compilers when optimizations are enabled, while
> non-conforming works on most compilers.

GCC may not be a good example of aggressive optimization. GCC's
generic targeting is rarely competative with well targeted specific
processor compiler. Penalties of 30% or so in the processors I regularly
work with.

The biggest weakness of many C compilers is in error reporting. Good
error test suites are much less common than functional testing and syntax
testing test suites.


Regards,

--
Walter Banks
Byte Craft Limited
http://www.bytecraft.com
wal...@bytecraft.com Canada


John Devereux

unread,
Jul 23, 2008, 11:06:42 AM7/23/08
to
"Wilco Dijkstra" <Wilco.remove...@ntlworld.com> writes:

> "John Devereux" <jdRE...@THISdevereux.me.uk> wrote in message news:87od4od...@cordelia.devereux.me.uk...
>> JSprocket <J...@internept.org> writes:
>>
>>> Nils wrote:
>>>
>>>> I Gcc got a better aliasing analysis backend recently that is more
>>>> strict about the rules. Lots of old code that depended on this
>>>> behaviour broke...
>>>
>>> In what sense is that "better"? A warning, perhaps, a switch for
>>> "strict" mode, but breaking existing code is a stupid thing to do.
>>
>> If that were always true, compilers could never improve. Breakage of
>> *incorrectly* written code is quite common. For example non-volatile
>> accesses to hardware, or "Optimising" of delay loops and time
>> sensitive code.
>
> Most compiler optimizations are independent of language semantics,
> so it's not true compilers could not improve if they had to be
> conservative.
> I know for a fact that one can beat compilers with aggressive optimizations
> (like GCC) by a huge margin while being very conservative.
> Of course if you don't use volatile correctly then it's your own
> fault.

But the optimisation possible on non-volatile variables *did* break a
lot of code that was out there (and still does, with every improvement
to this area of optimisation). So my point stands I think: a
"conservative" approach would have left all memory accesses alone, in
case they were "important".

> Non-conforming code is not the same though. In general incorrect code
> fails on most compilers when optimizations are enabled, while
> non-conforming works on most compilers.

I admit I am unclear about the difference here!

--

John Devereux

CBFalconer

unread,
Jul 23, 2008, 11:46:27 AM7/23/08
to
Frank Buss wrote:
>
... snip ...

>
> Is there any other standarized computer language, which is more
> restrictive defined? Like Java: There are no dangerous pointer
> operations, alignment and aliasing problems, the size of the
> basic types are fixed for every platform, the VM is simple and
> well defined etc. But Java is not a standard, e.g. like the
> ECMAScript standard (aka JavaScript). Looks like C# and the
> upcoming C++/CLI are interesting languages, but because of the
> CLI it would be difficult to use it for small microcontrollers.

Yes. For example, ISO10206. Note that a more complex language is
specified in a considerably shorter standard. Yet the language is
simpler to use. I am refraining from referencing ISO 7185 because
there are usage limitations there.

>
> Is it possible to define it more restrictive and still fast at
> all? If "int" would be defined 32 bit for all platforms, but the
> microcontroller has only 16 bit native registers, this would be
> slower than a C implementation.

As referenced above. In your example, if that size of operand is
required, the code writer should have specified the long type.
Failure to do so is simply a mistake.

Nils

unread,
Jul 23, 2008, 1:47:43 PM7/23/08
to

Want a warning for that? No problem.

gcc -Wstrict-aliasing ... does exactly what you're asking for.

Try some -Wstrict-overflow for extra fun. You may be surprised how often
you depend on non portable behaviour.

Nils


Nils

unread,
Jul 23, 2008, 1:57:25 PM7/23/08
to
Wilco Dijkstra wrote:

> There is no aliasing issue in the example. You would be right if he accessed
> both foo and p2 at the same time in a context where the obvious relation
> between the pointers is not visible

Hm... well. I'm not sure.. He's not explicitely casting from one type to
another (that one would be to easy). Instead he copies via a
self-written memcpy function.


However, he does his memcpy via the unit8_t type. Isn't that one a
built-in type these days? I only know that the compiler is forced to
assume aliasing if the data is cast to char* or volatile * something.

I don't have the slightest idea if the compiler can assume aliasing if
you copy via uint8_t. It's the same kind of data-type in the end, but
afaik char* is special because of the aliasing handling.

I don't have access to the standard, but I know some regular posters do
have access. Maybe they can chime in and take a look at this special case..


From the guts I'd still say it's an aliasing issue. I bet it goes away
if memcpy is rewritten to use char* instead of unit8_t*.

Cheers,
Nils

Frank Buss

unread,
Jul 23, 2008, 2:10:18 PM7/23/08
to
CBFalconer wrote:

> Yes. For example, ISO10206. Note that a more complex language is
> specified in a considerably shorter standard. Yet the language is
> simpler to use. I am refraining from referencing ISO 7185 because
> there are usage limitations there.

I think the Pascal syntax is too redundant and you have to think all the
time when you have to write ";" or when it is not allowed. And in the
ISO10206 text many parts are implementation-defined. Not only the size of
integer types, but important things like module activation order. The text
contains more than 50 occurances of "implementation-defined". This doesn't
look like a very restrictive definition, which allows to compile programs
for different implementations without modification, like it is possible
with Java.

> As referenced above. In your example, if that size of operand is
> required, the code writer should have specified the long type.
> Failure to do so is simply a mistake.

But this makes programming a lot harder. For writing portable programs, you
have to check each statement with the standard definition. In languages
like Java you can write tests for the corner cases for one compilation and
then you can be sure it works the same on every VM.

Nils

unread,
Jul 23, 2008, 2:19:50 PM7/23/08
to
John Devereux wrote:

> JSprocket <J...@internept.org> writes:
>> In what sense is that "better"? A warning, perhaps, a switch for
>> "strict" mode, but breaking existing code is a stupid thing to do.
>
> If that were always true, compilers could never improve. Breakage of
> *incorrectly* written code is quite common. For example non-volatile
> accesses to hardware, or "Optimising" of delay loops and time
> sensitive code.

Breakage of existing code is common.

There was quite some discussion about the fact that a new version of GCC
optimized checks away that depended on unspecified behaviour of integer
overflows.

That was around april. The CERT advice is still there but has been
revised multiple times (easy to find). The fun thing was that GCC got
blamed for beeing an unsecure compiler while tons of other compilers
applied the same optimization as well.

If you want a fun read/flamewar google for "Cert GCC". You'll find lots
of discussion if this was a bug in the "optimized away"-code or a bug in
the GCC-optimizer. Even the C-experts weren't sure who to blame for a
day or two..


Eric Smith

unread,
Jul 23, 2008, 3:05:48 PM7/23/08
to
Frank Buss wrote:
> I think the Pascal syntax is too redundant

What's redundant about it?

> and you have to think all the
> time when you have to write ";" or when it is not allowed.

The rule is different than C, but it isn't complicated. The semicolon
is used to separate two consecutive statements.

In fact, the rule in C is actually more complicated, even though most
of us are probably so accustomed to it that we don't normally think it
in detail.

> And in the ISO10206 text many parts are implementation-defined.

And that's different from C in what way?

Eric

Wilco Dijkstra

unread,
Jul 23, 2008, 3:30:13 PM7/23/08
to

"Nils" <n.pipe...@cubic.org> wrote in message news:48877105...@cubic.org...

> Wilco Dijkstra wrote:
>
>> There is no aliasing issue in the example. You would be right if he accessed
>> both foo and p2 at the same time in a context where the obvious relation
>> between the pointers is not visible
>
> Hm... well. I'm not sure.. He's not explicitely casting from one type to another (that one would be to easy). Instead
> he copies via a self-written memcpy function.

No, his example uses the built-in memcpy, and he says that it goes away
when he uses his own memcpy.

> From the guts I'd still say it's an aliasing issue. I bet it goes away if memcpy is rewritten to use char* instead of
> unit8_t*.

There is nothing special about uint8_t, it's just a typedef in <stdint.h>.

I don't believe it is an aliasing issue. I can't reproduce it with the EABI
GCC4.2, so it's more likely a bug in memcpy or an alignment issue
(older non-EABI versions used some weird structure alignment rules,
making Bar a 4-byte structure...).

Wilco


Nils

unread,
Jul 23, 2008, 3:36:11 PM7/23/08
to
Wilco Dijkstra wrote:
> I don't believe it is an aliasing issue. I can't reproduce it with the EABI
> GCC4.2, so it's more likely a bug in memcpy or an alignment issue
> (older non-EABI versions used some weird structure alignment rules,
> making Bar a 4-byte structure...).

True..

Now a look at the assembly output of his test-case would be interesting ...


Nils

Wilco Dijkstra

unread,
Jul 23, 2008, 4:14:41 PM7/23/08
to

"CBFalconer" <cbfal...@yahoo.com> wrote in message news:48872784...@yahoo.com...

> Wilco Dijkstra wrote:
>> "Frank Buss" <f...@frank-buss.de> wrote:
>>> Nils wrote:
>>>
>>>> This is not strictly a bug..
>>>>
>>>> You're violating the C-rules of aliasing. In short: You must
>>>> not modify a value/stucture of type A and read from the same
>>>> memory by casting a pointer from A to B. The compiler is
>>>> allowed to assume that because the types differ the two
>>>> pointers point to different memory.
>>>
>>> Can you cite the chapters in the standard? My first feeling was
>>> the same, because using void* worked, but it works with Visual
>>> C for ARM, which I think does a good register optimization, too,
>>> and Wilco says, it is not an aliasing problem.
>>
>> What the standard says is not relevant. The C standards are
>> consistently and deliberately underspecified, unclear and are
>> more concerned with features that were obsolete 20 years ago
>> (like ones-complement and signed overflow) than actually
>> specifying important features like bitfields. So compiler
>> writers typically add their own rules, the most important of
>> which says "thou shalt correctly compile existing code".
>
> What a horrible (and dangerous) attitude. Not only that, but
> inaccurate also.

I would call adding optimizations that break perfectly valid code
horrible and dangerous...

> One of the great problems with C is the failure to catch overflow
> in signed integral expressions. It is not solvable, because of the
> necessity of supporting older code. Bitfields are unimportant,
> because there are adequate replacements available in the form of
> constants and bit manipulation operations.

There is little value in catching overflows (apart from blowing up
expensive rockets in spectacular ways) while the complexity and
cost of implementing asynchronous exceptions is enormous.

Bitfields are easier to use than complex bit manipulation code,
so they are useful. If I use your argument then why do we have
if statements and loops as you can achieve the same with goto?

> One of the strengths of C is the fact that code that meets the
> requirements of the standard is almost always totally portable. By
> itself this is not the final word, but it makes it easy to isolate
> and emphasize what code needs modification during a port.

No non-trivial piece of code meets all requirements of the standard.
Have you ever used lint or compiled some application with the strict
conformance checking option? You will be surprised. Even if it does
compile, it will still contain implementation defined behaviour which
makes it theorhetically unportable. In practise most non-conforming
code is portable, so there is no advantage at all in being conformant.

Wilco


Wilco Dijkstra

unread,
Jul 23, 2008, 4:43:00 PM7/23/08
to

"Eric Smith" <er...@brouhaha.com> wrote in message news:m3wsjce...@donnybrook.brouhaha.com...

> Frank Buss wrote:
>> I think the Pascal syntax is too redundant
>
> What's redundant about it?
>
>> and you have to think all the
>> time when you have to write ";" or when it is not allowed.
>
> The rule is different than C, but it isn't complicated. The semicolon
> is used to separate two consecutive statements.
>
> In fact, the rule in C is actually more complicated, even though most
> of us are probably so accustomed to it that we don't normally think it
> in detail.

A well defined syntax would allow either. Or make the whole problem
go away by not requiring separators/terminators - in almost all cases
they are redundant. Parsing is a well understood problem.

>> And in the ISO10206 text many parts are implementation-defined.
>
> And that's different from C in what way?

I think he's just confirming that other languages have similar undefined
and/or implementation defined issues as C has. Ie. so called safe
languages are not actually much more portable or safer than C, even if
they appear to be. Java is not immune to it either (the correctness of
some programs depends on when exactly garbage collection occurs).

Wilco


Wilco Dijkstra

unread,
Jul 23, 2008, 5:24:27 PM7/23/08
to

"Walter Banks" <wal...@bytecraft.com> wrote in message news:48873A6D...@bytecraft.com...

>
>
> Wilco Dijkstra wrote:
>
>>
>> > If that were always true, compilers could never improve. Breakage of
>> > *incorrectly* written code is quite common. For example non-volatile
>> > accesses to hardware, or "Optimising" of delay loops and time
>> > sensitive code.
>>
>> Most compiler optimizations are independent of language semantics,
>> so it's not true compilers could not improve if they had to be conservative.
>> I know for a fact that one can beat compilers with aggressive optimizations
>> (like GCC) by a huge margin while being very conservative.
>>
>> Of course if you don't use volatile correctly then it's your own fault.
>> Non-conforming code is not the same though. In general incorrect code
>> fails on most compilers when optimizations are enabled, while
>> non-conforming works on most compilers.
>
> GCC may not be a good example of aggressive optimization. GCC's
> generic targeting is rarely competative with well targeted specific
> processor compiler. Penalties of 30% or so in the processors I regularly
> work with.

That was my point. GCC does have a lot of advanced high-level
optimizations (including ones that unnecessarily break code), but it's
very bad at doing target aware optimizations.

> The biggest weakness of many C compilers is in error reporting. Good
> error test suites are much less common than functional testing and syntax
> testing test suites.

That's partly due the less professional compilers using LR parser
generators rather than handcrafting a recursive descent parser, and partly
due to it being a lot of work. It's funny how easy it is to crash the frontend of
any compiler just by giving it random input.

Wilco


Wilco Dijkstra

unread,
Jul 23, 2008, 6:08:25 PM7/23/08
to

"John Devereux" <jdRE...@THISdevereux.me.uk> wrote in message news:87fxq0d...@cordelia.devereux.me.uk...

You're right, but being *that* conservative would mean you couldn't do
any optimization! Volatile is one of those things which most people
understand intuitively and which is implemented almost identically in all
compilers (despite being almost completely unspecified in C). So it's
perfectly reasonable to expect programmers to use it properly.

So with being conservative I mean optimizing as much as possible
without breaking existing software. Even if the language standard
allows us to break most software, it's commercial suicide. Basically
you weigh the benefit vs the cost of lots of angry customers. Would
you like to pay lots of money for a compiler, and be told to rewrite all
your existing software just because it isn't 100% conformant?

>> Non-conforming code is not the same though. In general incorrect code
>> fails on most compilers when optimizations are enabled, while
>> non-conforming works on most compilers.
>
> I admit I am unclear about the difference here!

Conforming = adhering to the language standard.
Correct = working as intended/specified.

Wilco


CBFalconer

unread,
Jul 23, 2008, 8:23:18 PM7/23/08
to
Wilco Dijkstra wrote:
> "Nils" <n.pipe...@cubic.org> wrote:
>
... snip ...

>
>> From the guts I'd still say it's an aliasing issue. I bet it goes
>> away if memcpy is rewritten to use char* instead of unit8_t*.
>
> There is nothing special about uint8_t, it's just a typedef in
> <stdint.h>.

Yes there is something special. It doesn't exist in C89/C90/C95
compilers, requiring C99 systems. And it doesn't exist if CHAR_BIT
is larger than 8, i.e. a byte/char fills more than 8 bits. Using
it reduces portability.

CBFalconer

unread,
Jul 23, 2008, 8:38:28 PM7/23/08
to
Wilco Dijkstra wrote:
> "CBFalconer" <cbfal...@yahoo.com> wrote:
>
... snip ...

>
>> One of the strengths of C is the fact that code that meets the
>> requirements of the standard is almost always totally portable. By
>> itself this is not the final word, but it makes it easy to isolate
>> and emphasize what code needs modification during a port.
>
> No non-trivial piece of code meets all requirements of the standard.
> Have you ever used lint or compiled some application with the strict
> conformance checking option? You will be surprised. Even if it does
> compile, it will still contain implementation defined behaviour which
> makes it theorhetically unportable. In practise most non-conforming
> code is portable, so there is no advantage at all in being conformant.

I routinely compile everything with virtually all error catching
enabled. If something appears that is not portable, I either
rewrite it to be portable (prefereable, unless there is a
significant performance penalty) or remove it to an isolated
<system-dependant> module. You will be surprised how portable your
code becomes, and how easy such writing becomes.

I eschew long functions, and I use the following alias (single
line) to execute gcc as cc:

gcc -W -Wall -ansi -pedantic -Wwrite-strings -Wfloat-equal -gstabs+
-ftrapv

CBFalconer

unread,
Jul 23, 2008, 8:50:57 PM7/23/08
to
Nils wrote:
> JSprocket wrote:
>
... snip ...

>
>> In what sense is that "better"? A warning, perhaps, a switch for
>> "strict" mode, but breaking existing code is a stupid thing to do.
>
> Want a warning for that? No problem.
>
> gcc -Wstrict-aliasing ... does exactly what you're asking for.
>
> Try some -Wstrict-overflow for extra fun. You may be surprised how
> often you depend on non portable behaviour.

I can find -Wstrict-aliasing for my gcc, but no luck for
strict-overflow. At what version did it appear?

Wilco Dijkstra

unread,
Jul 23, 2008, 9:46:47 PM7/23/08
to

"CBFalconer" <cbfal...@yahoo.com> wrote in message news:4887CB76...@yahoo.com...

> Wilco Dijkstra wrote:
>> "Nils" <n.pipe...@cubic.org> wrote:
>>
> ... snip ...
>>
>>> From the guts I'd still say it's an aliasing issue. I bet it goes
>>> away if memcpy is rewritten to use char* instead of unit8_t*.
>>
>> There is nothing special about uint8_t, it's just a typedef in
>> <stdint.h>.
>
> Yes there is something special. It doesn't exist in C89/C90/C95
> compilers, requiring C99 systems. And it doesn't exist if CHAR_BIT
> is larger than 8, i.e. a byte/char fills more than 8 bits. Using
> it reduces portability.

Neither is an issue as many compilers added <stdint.h> without fully
supporting C99, and CHAR_BIT not being equal to 8 is quickly
becoming as rare as ones-complement (the one unfortunate thing
compilers can't seem to agree on is the signedness of char...).

A lot of existing code defines sized typedefs already for portability
(like uint32). It's unfortunate it took so long for the C standard to
support sized types, but better late than never...

Wilco


Grant Edwards

unread,
Jul 23, 2008, 9:53:50 PM7/23/08
to
On 2008-07-24, Wilco Dijkstra <Wilco.remove...@ntlworld.com> wrote:

>> Yes there is something special. It doesn't exist in C89/C90/C95
>> compilers, requiring C99 systems. And it doesn't exist if CHAR_BIT
>> is larger than 8, i.e. a byte/char fills more than 8 bits. Using
>> it reduces portability.
>
> Neither is an issue as many compilers added <stdint.h> without
> fully supporting C99, and CHAR_BIT not being equal to 8 is
> quickly becoming as rare as ones-complement

It's still very common in both floating point and integer DSPs.

--
Grant Edwards grante Yow! Half a mind is a
at terrible thing to waste!
visi.com

Nils

unread,
Jul 23, 2008, 10:54:19 PM7/23/08
to
CBFalconer wrote:

> > I can find -Wstrict-aliasing for my gcc, but no luck for
> strict-overflow. At what version did it appear?

That came with 4.1.something as far as I remember. It was one reaction
to the Cert vs. Gcc discussion.

Cheers,
Nils


jet...@hotmail.com

unread,
Jul 24, 2008, 4:16:55 AM7/24/08
to
> 6 struct Foo {
> 7 uint8_t x;
> 8 uint8_t y;
> 9 uint8_t z;
> 10 uint8_t m[3];
> 11 };
> 12
> 13 struct Bar
> 14 {
> 15 uint8_t m[3];
> 16 };

> 33 Bar *p1 = &bar;
> 34 Bar *p2 = (Bar*)(foo->m);

Here is your bug:

The m member of struct Foo is not the same as a struct Bar. It's up
to the compiler how to pack the members in a struct. It could, for
example, add a header before the first member, which would be
sufficient to make your code break.

You can rewrite your code like this:

struct Bar {
uint8_t m[3];
};

struct Foo {
uint8_t x;
uint8_t y;
uint8_t z;
struct Bar bar;
};


struct Bar *p1 = &bar;
struct Bar *p2 = &(foo->bar);

Then your problem should disappear.

Frank Buss

unread,
Jul 24, 2008, 5:10:42 AM7/24/08
to
jet...@hotmail.com wrote:

> Here is your bug:
>
> The m member of struct Foo is not the same as a struct Bar. It's up
> to the compiler how to pack the members in a struct. It could, for
> example, add a header before the first member, which would be
> sufficient to make your code break.

This is not the bug, because the own written memcpy function works. I think
the bug is, that the wrong memcpy function is selected, but the assembler
output has to be analyzed and compared with e.g. your code, or the void*
solution I proposed, for verifying this assumption. Maybe someone who has
more time than I have could do this.

John Devereux

unread,
Jul 24, 2008, 5:17:54 AM7/24/08
to
"Wilco Dijkstra" <Wilco.remove...@ntlworld.com> writes:

> "John Devereux" <jdRE...@THISdevereux.me.uk> wrote in message news:87fxq0d...@cordelia.devereux.me.uk...
>> "Wilco Dijkstra" <Wilco.remove...@ntlworld.com> writes:
>>
>>> "John Devereux" <jdRE...@THISdevereux.me.uk> wrote in message news:87od4od...@cordelia.devereux.me.uk...

[...]


>>>> If that were always true, compilers could never improve. Breakage of
>>>> *incorrectly* written code is quite common. For example non-volatile
>>>> accesses to hardware, or "Optimising" of delay loops and time
>>>> sensitive code.
>>>
>>> Most compiler optimizations are independent of language semantics,
>>> so it's not true compilers could not improve if they had to be
>>> conservative.
>>> I know for a fact that one can beat compilers with aggressive optimizations
>>> (like GCC) by a huge margin while being very conservative.
>>> Of course if you don't use volatile correctly then it's your own
>>> fault.
>>
>> But the optimisation possible on non-volatile variables *did* break a
>> lot of code that was out there (and still does, with every improvement
>> to this area of optimisation). So my point stands I think: a
>> "conservative" approach would have left all memory accesses alone, in
>> case they were "important".
>
> You're right, but being *that* conservative would mean you couldn't do
> any optimization!

Then we agree - that was the point I was making.

> Volatile is one of those things which most people
> understand intuitively and which is implemented almost identically in all
> compilers (despite being almost completely unspecified in C). So it's
> perfectly reasonable to expect programmers to use it properly.
>
> So with being conservative I mean optimizing as much as possible
> without breaking existing software. Even if the language standard
> allows us to break most software, it's commercial suicide. Basically
> you weigh the benefit vs the cost of lots of angry customers. Would
> you like to pay lots of money for a compiler, and be told to rewrite all
> your existing software just because it isn't 100% conformant?

No... but then I use gcc :) :)

Seriously, one thing gcc & friends does let you do easily is run
different versions for each project, if you want to. So you can
develop a project with gcc-x.y and, perhaps years later, make
modifications under that *same* version. You don't have to worry about
your dongle not being compatible with your new machine or new windows
or new network card, with the supplier only selling a version that
won't compile your code any more.

You can version control the compiler binaries along with the project,
or just the script needed to build them (since the sources look likely
to remain available forever).

Of course, it is still better if optimisations can be made *without*
breaking existing code.

>>> Non-conforming code is not the same though. In general incorrect code
>>> fails on most compilers when optimizations are enabled, while
>>> non-conforming works on most compilers.
>>
>> I admit I am unclear about the difference here!
>
> Conforming = adhering to the language standard.
> Correct = working as intended/specified.

OK, thanks.

--

John Devereux

Wilco Dijkstra

unread,
Jul 24, 2008, 6:46:44 AM7/24/08
to

"Frank Buss" <f...@frank-buss.de> wrote in message news:zvosgsb7jls4.a...@40tude.net...

> jet...@hotmail.com wrote:
>
>> Here is your bug:
>>
>> The m member of struct Foo is not the same as a struct Bar. It's up
>> to the compiler how to pack the members in a struct. It could, for
>> example, add a header before the first member, which would be
>> sufficient to make your code break.

It wouldn't be allowed to do that of course, or lots of code would break. However
if the GCC version is old it may use the old calling standard with its weird
structure alignment. This would make Bar a 4-byte struct with 4-byte alignment,
which causes alignment issues. Since the OP didn't report a crash due to a
misaligned load, it's not clear that is the problem though.

> This is not the bug, because the own written memcpy function works. I think
> the bug is, that the wrong memcpy function is selected, but the assembler
> output has to be analyzed and compared with e.g. your code, or the void*
> solution I proposed, for verifying this assumption. Maybe someone who has
> more time than I have could do this.

I thought you reproduced the problem, could you post the assembly output for
cp()? I can't reproduce it with GCC4.2, but with the assembler it will be easy
to diagnose.

Wilco


Michael N. Moran

unread,
Jul 24, 2008, 7:11:57 AM7/24/08
to
CBFalconer wrote:
> I routinely compile everything with virtually all error
> catching enabled.

Yup... Saves *much* trouble down-the-road *and*
encourages developers learn the nuances of the
language.

[snip]

> I eschew long functions, and I use the following alias
> (single line) to execute gcc as cc:
>
> gcc -W -Wall -ansi -pedantic -Wwrite-strings
> -Wfloat-equal -gstabs+ -ftrapv

What? No -Werror ?


--
Michael N. Moran (h) 770 516 7918
5009 Old Field Ct. (c) 678 521 5460
Kennesaw, GA, USA 30144 http://mnmoran.org

"So often times it happens, that we live our lives in chains
and we never even know we have the key."
"Already Gone" by Jack Tempchin (recorded by The Eagles)

The Beatles were wrong: 1 & 1 & 1 is 1

Wilco Dijkstra

unread,
Jul 24, 2008, 7:14:24 AM7/24/08
to

"CBFalconer" <cbfal...@yahoo.com> wrote in message news:4887CF04...@yahoo.com...

> Wilco Dijkstra wrote:
>> "CBFalconer" <cbfal...@yahoo.com> wrote:
>>
> ... snip ...
>>
>>> One of the strengths of C is the fact that code that meets the
>>> requirements of the standard is almost always totally portable. By
>>> itself this is not the final word, but it makes it easy to isolate
>>> and emphasize what code needs modification during a port.
>>
>> No non-trivial piece of code meets all requirements of the standard.
>> Have you ever used lint or compiled some application with the strict
>> conformance checking option? You will be surprised. Even if it does
>> compile, it will still contain implementation defined behaviour which
>> makes it theorhetically unportable. In practise most non-conforming
>> code is portable, so there is no advantage at all in being conformant.
>
> I routinely compile everything with virtually all error catching
> enabled. If something appears that is not portable, I either
> rewrite it to be portable (prefereable, unless there is a
> significant performance penalty) or remove it to an isolated
> <system-dependant> module. You will be surprised how portable your
> code becomes, and how easy such writing becomes.

Well you're unlike many of us who inherit many thousands of lines of existing
code and have to live with it. Some of this code doesn't even compile
when you change or upgrade compilers, so one often has to explicitly
disable certain errors and substitute one language extension with another.
However when it does compile it usually works without any problems.

Wilco


0 new messages