Gnu tools for ARM Cortex development

It isn't entirely false either; sometimes buggy code (e.g.uninitialized variables) appears to work fine with optimizations turned off but fails when using the optimizer. When code breaks when changing compiler settings I'm more inclined to suspect the code than the compiler, even though I have been bitten by compiler bugs in the past.

This problem is not unique to GCC, I have seen the same problem with some commercial compilers which produced clearly incorrect code when using the more aggressive optimization levels.

Reply to
Dombo
Loading thread data ...

I have never seen a situation where correct C code failed because of higher optimisation except when exact timing is needed, or in the case of compiler errors. I have seen lots of code where the author has said it works without optimisations, but fails when they are enabled - in every case, it was the code that was at fault.

The most common issue is a misunderstanding of how "volatile" works. Some people have all sorts of believes about "volatile" giving atomic access to data, or that it "disables optimisations". Another favourite is the belief that a volatile access (or inline assembly code or intrinsic function, such as an interrupt disable) acts as a memory barrier.

I've also seen code where people try to limit the compiler in an attempt to control optimisations, using "tricks" like calling functions through function pointers to force the compiler to avoid inlining or other optimisations. Then they get caught out when a newer, smarter version of the compiler can see through that trick.

There /are/ times when you want more precise control over the code generation - perhaps there are issues with code size or stack size, precise timing requirements (too fast can be as bad as too slow), or integration with assembly or external code that depends on exact generated code. Proper use of volatile and memory barriers is usually enough to write correct code, and is the best solution - it is written in the source code, and is independent of optimisation settings. Occasionally assembly code or specific optimisation settings for specific modules are required, but these cases are very rare.

Note that this is all independent of the compiler - there is nothing gcc-specific about these issues. However, I've seen many "optimisation broke my code" questions on gcc mailing lists, because people have ported code from compilers with weaker code generators (including earlier gcc versions) and their code now breaks, because gcc is smarter. The archetypical example is "my delay loop worked with compiler X, but gcc skips it".

A lot of code that is widely used is /not/ perfectly good. For most embedded development with gcc, the standard optimisation is "-Os" which is "-O2" but with a little more emphasis on code size, and avoiding those optimisations that increase the code size for relatively little speed gain (such as a lot of loop unrolling, or aggressive inlining). The standard for desktop and "big" systems is "-O2".

Level "-O3" is seldom used in practice with gcc, but it is not because the compiler is unstable or generates bad code. It is simply that the additional optimisations used here often make the code bigger for very little, if any, speed gain. Even on large systems, bigger code means more cache misses and lower speed. So these optimisations only make sense for code that actually benefits from them.

There /are/ optimisations in gcc that are considered unstable or experimental - there are vast numbers of flags that you can use if you want. But they don't get added to the -Ox sets unless they are known to be stable and reliable and to help for a wide range of code. Flags that are known to be experimental or problematic are marked as such in the documentation.

Of course, there is no doubt that gcc has its bugs. And many of these occur with the rarer optimisation flags - that's code that has had less time to mature and had less testing than code that is used more often. The gcc bug trackers are open to the public, feel free to look through them or register any bugs you find.

Now, how is this different from any other compiler? The big difference is that with gcc, everything is open. When people are having trouble, they will often discuss it in a public mailing list or forum. When a bug is identified, it is reported in a public bug tracker. With a commercial compiler, when a user thinks they have hit a bug they will often talk to the vendor's support staff, who will help identify the bug, fix it, and perhaps ship out a fixed version to the user and include the fix in later releases. The end result is similar - the bug gets found and fixed. But with gcc, everyone (at least, everyone who is interested!) knows about the bug - with the commercial compiler, other users are blissfully unaware. Thus people can see that gcc has bugs - very few of which they would ever meet in practice, while with the commercial compiler they know of no bugs - and also meet very few in practice. There are a few commercial compiler vendors that publish detailed lists of bugs fixed in their change logs or release notes - these are similar to the lists published with new versions of gcc.

I expect at this point somebody is going to say that commercial vendors have better testing routines than gcc, and therefore less bugs. There is no objective way to know about the different testing methodologies, or their effectiveness at finding bugs, so any arguments one way or the other are futile. There is also such a wide range of commercial tools that any generalisation is also meaningless. There are commercial tools that I know have good procedures, though they still have bugs. There are other commercial tools that I know have far lower testing and qualification standards than gcc.

Another difference with gcc is to consider the code that is compiled with it. My guess is that much of the code that people consider "risky to compile with optimisation" is from open source software, typically running on *nix machines - after all, this covers a very large percentage of the use of gcc. It should be remembered that although a lot of open source software is very good quality, a lot is not. A great deal of software for desktop use (open source or closed source, it matters not) is not written with the level of quality that we expect in embedded systems. There are also lower standards on what is good enough for use - failing to compile and run cleanly when optimised shows that the code is broken, but it may still be good enough for the job it does.

Reply to
David Brown

There is another rational for this. Most compiler developers including GCC developers spend most of their development time working on simple code fragments. As a side effect code written in short clear statements very often produce some of the nicest generated code. This doesn't make the statement incorrect.

I ran into an extreme example of this a few years ago when a previously unknown compiler vendor published some common benchmark results that were exceptionally good. Turns out the compiler could only process simple statements and the benchmarks were recoded to fit the compiler limitations.

Regards,

Walter..

-- Walter Banks Byte Craft Limited

formatting link

Reply to
Walter Banks

That may be true to some extent - especially when looking at backends. When you are trying to improve peepholes or register allocation on an avr port, it's easier to test and study short sequences.

On the other hand, gcc is used for a huge variety of code and target types. For example, gcc is almost invariably compiled with gcc. The gcc developers therefore re-compile their compilers on a regular basis, and will put at least some effort into the quality of object code generated from the gcc source. And if you've ever looked at the source code of gcc, there are areas that are about as far from "short clear statements" as you could possibly get! This is one of the advantages of gcc being a general and flexible compiler (you've already pointed out the disadvantages compared to dedicated small processor compilers) - the front end of gcc is tested with some of the biggest and most complex source code around.

Reply to
David Brown

I agree that *most* problems are in the program. However, I have seen correct code fail (and not just C code) under optimization and plenty of genuine compiler bugs as well. The more advanced the optimization, the more likely there will be bugs in the implementation.

Then too, people who don't work on compilers often don't appreciate just how difficult it is to predict the effects of mixing optimizations and the shear impossibility of testing all the possible combinations. If you look inside any optimizing compiler, for each optimization you'll see a myriad of conditions that prevent it from being applied or which modify how it is applied. All it takes is for one bad condition to be missed and some poor programmer is guaranteed a headache.

Agreed. But there are also a number very carefully coded libraries that are known to be incompatible with certain versions of GCC.

It isn't. But this thread was about GCC in particular.

Every compiler ... like every program ... has bugs (possibly latent) and every optimizing compiler has combinations of optimizations that don't work under all circumstances. The problem is the circumstances under which they don't work may not all be prohibited and are rarely documented.

GCC may have a switch for every possible optimization - but how is the programmer to know which combinations will cause problems given the idiosyncrasies of a particular piece of code?

There may be a one liner in the manual saying that A doesn't work with B, but what happens if you select both? If you're lucky you'll get a warning, but sometimes one or both optimizations silently are not applied and on rare occasions they may interact to produce bad code.

I don't find any particular fault with GCC's testing methodology. What does concern me is the attempt to be all things to all people ... GCC is a unified IR trying to support the differing semantics of several source languages across dozens of disparate targets.

Others have tried to support multiple languages with a common backend

- IBM, DEC, Sun, Pr1me, to name a few - and historically it has not worked as well as they would have liked ... even without the difficulties of supporting many different targets.

Even today, there are compilers for many languages targeting JVM and/or .NET, but if you look closely, you'll find that many implement subset or derivative languages because the semantics of the "standard" language are incompatible with the platform and the developer has judged the features too costly to emulate. Of the full language implementations available, about half are interpreters.

George

Reply to
George Neuner

I agree with you here in that advanced optimisations are often a hot-spot for bugs in the compiler.

But I think (or at least, I /like/ to think) for more risky optimisations, compiler developers are more conservative about when they are applied - either by being careful about deciding when the optimisation can be applied, or by requiring an explicit flag to enable it. In the case of gcc, there should be no risky optimisations enabled by any -Ox flag - you have to give the -fxxx flag explicitly.

Yes, sometimes that's the case. It's not uncommon to have a minimum version requirement - if the code takes advantage of newer ISO standards, or newer gcc extensions, it won't work with older versions. The other way round is less common but does happen. Sometimes it's for banal reasons, such as new keywords in later C standards conflicting with identifiers (though this can be avoided by being more careful with flag settings in makefiles).

Basically, if you use additional optimisation switches, you are taking a risk. If it was a well-tested and low risk optimisation that can be expected to improve the generated code for a lot of code, then it would be included in one of the -Ox levels. (The exception to this is for switches that are known to work well, but break standards in some way -

-ffast-math is a good example. It gives you smaller and faster code, but does not comply with IEEE standards.) So if you think an extra optimisation or two will help your code, you can try it and see - but expect to be more careful about testing.

It's hard to do this well - and it's clear that it causes problems for gcc for some backends. Many of the targets of gcc are in fact similar in principle - 32-bit, RISC, lots of registers, load-store architecture. But if you look at something like the avr port of gcc, you can see that the code is often somewhat suboptimal - the front-end of gcc pushes

8-bit calculations up to 16-bit ints (as per the C standards), and the backend has to work out when parts of the 16-bit double-register operations can be omitted. It does a good job, but not perfect, and the lost information means the register set is not fully utilised.

I think aiming for this sort of independence between stages of the compiler is a good thing overall. You are never going to get the best compiler for the awkward smaller cpus (like the 8051) using such a general compiler. But by structuring the compiler in this way you get to re-use the parts rather than re-inventing them for each new target. And you get the flexibility of being able to choose the target processor, the host processor, and the languages all fairly independently (and even pick a different host on which to build the compiler!).

The other big project, and gcc's main competitor, is llvm. It is even stricter than gcc about separating the stages of the compiler, and more flexible about using different front-ends or back-ends.

Reply to
David Brown

In that case, it wasn't correct C code to begin with. Relying on exact timing of any code sequence written in C is a design error.

Reply to
Hans-Bernhard Bröker

I only partly agree here. Sometimes you /do/ need exact timing in software, and though C is a poor tool for such code, it may still be the best you've got. So I'd say that it's a weak point in the design that needs extra care, but not necessarily an error as it may be better than any practical alternatives.

Reply to
David Brown

Probably more accurately relying on instruction execution timing is a design error. Our automotive customers write engine controller code in C with a requirement that timing events be accurate. This is done using processor based timers rather than relying on execution timing.

Reliable event timing can be achieved as a combination of hardware and software. Outputs are preprogrammed to be transferred to a pin on the arrival of an event (a timer for example) and inputs are sampled and held on an event waiting for further processing. This approach has essentially removed timing errors in applications written in either C or asm.

None of this is new. The Z8 and COP8 microprocessors used timer events for serial port timing 20 plus years ago.

Regards,

Walter..

-- Walter Banks Byte Craft Limited

formatting link

--- news://freenews.netfront.net/ - complaints: snipped-for-privacy@netfront.net ---

Reply to
Walter Banks

Just because it's the best one's got doesn't mean it deserves to be qualified as "correct C code". Code that fulfills its requirements by coincidence as much as by design cannot possibly be correct.

Some jobs just can't be done correctly (or done at all) in C. Exact timing is about the most obvious example in that category.

Reply to
Hans-Bernhard Bröker

I have been told that the Keil is marginally better than the IAR. This was last summer, so things might have changed. At that time the gcc for CM3 really sucked.

Would like to know if gcc has improved.

--
Best Regards
Ulf Samuelsson
 Click to see the full signature
Reply to
Ulf Samuelsson
[...]

for the CM3, I assume?

Can't tell for CM3, and not for Keil or IAR, but I currently test gcc (CodeSourcery) and CodeWarrior for Coldfire.

Until now, I found no stupid piece of code. gcc even detected that in

|static const unsigned int len[] = {16, 16, 32}; |... |foo = (foo + 1) % len[1];

Buflen[1] is constant and used the appropriate andi (and immediate) instruction, where CodeWarrior used the much slower remu (remainder).

Besides this, I found no noticeable differences in code quality, but I hadn't yet the time to test in depth.

I already gave up on libraries since I need them really lightweight, e.g. without locale, so I will make my own (don't need many or complicated functions).

Oliver

--
Oliver Betz, Munich
despammed.com is broken, use Reply-To:
Reply to
Oliver Betz

Yes, that was what I meant.

--
Best Regards
Ulf Samuelsson
 Click to see the full signature
Reply to
Ulf Samuelsson

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.