Log in

View Full Version : Whacky C Complier behavior


Larry Barello
13-01-2004, 00:40
Beware! The compiler assumes constants are of type "char"

For example, this will generate no errors and give you -12 as the results!

int InitFoo = (100 * 5);

To get the right answer, you need to cast the 100 as an int:

int InitFoo = (int)100 * 5;

Sigh.

P.S. - floating point constants are completely screwed up. Not even casting seems to help.

Mike Betts
13-01-2004, 08:26
Larry,

2 issues here...

You are doing an implicit initialization. Never a good idea in an embedded system.

Make it:

int InitFoo;

for your declaration and then

InitFoo = (100 * 5);

in your initialization routine.

Secondly (IMHO), you really do not want to do floating point... I have posted about this in other threads.

Good luck.

john atwood
13-01-2004, 09:32
2 issues here...

You are doing an implicit initialization. Never a good idea in an embedded system.

Make it:

int InitFoo;

for your declaration and then

InitFoo = (100 * 5);

in your initialization routine.

Secondly (IMHO), you really do not want to do floating point... I have posted about this in other threads.

Good luck.


The initialization looks pretty explicit to me.
Also, why waste code space and start up time
doing math the compiler can do.

The wacky behavior is a feature, where C18 intentionally departs from ISO standard behavior
for efficiency reasons. See section 2.7.1 of the C18 users guide.

To change the behavior, use the -Oi command line option, which can be set in MPLAB under Project->Build Options->Project->(tab)MPLAB C18->(check)Enable Integer Promotions.


John

Larry Barello
13-01-2004, 10:06
...

The wacky behavior is a feature, where C18 intentionally departs from ISO standard behavior
for efficiency reasons. See section 2.7.1 of the C18 users guide.

To change the behavior, use the -Oi command line option, which can be set in MPLAB under Project->Build Options->Project->(tab)MPLAB C18->(check)Enable Integer Promotions.


John
Thanks!

Can you explain why not promoting to integer is more efficient? Or does that impact run time calculations even though the target may be a char? Can I override the promotion by explicitely typing intermediate values in an expression?

P.S. I figured out the Floating point problems: One has to tell MPLAB that the floating point representation is "MCHP" not ieee - then things display Ok. Unfortunately that only seems to work in the "watch" window properties. In the regular listings, hovering the cursor over the variable will return gibberish.

WizardOfAz
21-01-2004, 10:43
Can you explain why not promoting to integer is more efficient? Or does that impact run time calculations even though the target may be a char?

At its heart, it's an 8 bit chip. To do arithmetic on more than 8 bits the compiler has to generate a sequence of operations, manage the carry bit, and so on. That's why working with 8 bit values is more efficient if what you're doing can fit in 8 bits.

As for declared initial values (called implicit initialization in an earlier post) - see next post.

Bill

WizardOfAz
21-01-2004, 11:11
You are doing an implicit initialization. Never a good idea in an embedded system.

Make it:

int InitFoo;

for your declaration and then

InitFoo = (100 * 5);

in your initialization routine.


I guess I'm going to disagree about the "never a good idea" comment. If the environment supports initialization, then the provided initialization mechanism will likely be more efficient (faster and smaller) than doing the initialization in your own initialization code.

I started wondering how C18 and the FRC runtime environment was handling this issue, since ANSI C both supports initialization and specifies that unitialized static data is set to zero. Here's what I found. If I've got it wrong, somebody tell me.

First, C18 ships with three versions of startup code:
c018.o which does no initialization of data and is onlyl about 1k bytes big
co18i.o which initializes static data to declared initial values, about 4k bytes
co18z.o which initializes static data to declared initial values, and zeros the rest of the static data, also about 4k bytes

Which one you use is determined by your linker script.

When you look there, you'll see that none of them are used if you're using the linker script shipped with IFI default code. Instead, the project includes ifi_startup.c, which in fact is mostly assembler embedded in a C source file. This file has the startup code, and you'll see that it does both clear memory to zero and also copy initialized data to ram. It does these both with block copies so the code is very tiny.

So at least in our FRC and EDURC environments, you can be assured that static declarations (data declared outside of a function) like this
int foo = 10;
int bar;
will result in foo being initially 10 and bar being initially zero. This initialization is done only once at power-on-reset, not each time the function is called.

If these declaration are inside a function, they are not static data. If they have declared initial values, the compiler generates code to store the initial value each time the function is called; If they have no initial value, then there is none. This is non-static data and the C18 compiler does nothing to make its value predictable. This does not violate the ANSI standard.
so in

int foobar() {
int foo = 10;
int bar;
...
}

foo will be set to 10 every time foobar is called, but bar will have no predictable initial value.

Bottom line is that this environment does initialization according to the standard. As long as you understand how static and non-static initialization differ, it's safe to use.

Bill

deltacoder1020
21-01-2004, 12:29
At its heart, it's an 8 bit chip. To do arithmetic on more than 8 bits the compiler has to generate a sequence of operations, manage the carry bit, and so on. That's why working with 8 bit values is more efficient if what you're doing can fit in 8 bits.

exactly - a char-char ADD op is a single instruction, whereas an int-int add requires two (and that's assuming the second ADD op automatically utilizes the set carry bit).

Mike Betts
21-01-2004, 13:43
I guess I'm going to disagree about the "never a good idea" comment. If the environment supports initialization, then the provided initialization mechanism will likely be more efficient (faster and smaller) than doing the initialization in your own initialization code.

Wiz,

I guess I'm just too conservative. I have worked with a lot of processors in a lot of environments in multiple languages. I never trust the compiler options in areas like these.

If you have ever spent a week of sleepless nights rewriting code to be explicit rather than implicit because the new compiler (or even version of the compiler) does not compile like the old one did, you would be as gun shy as me.

Other things I do not do:
1. Depend on variable promotion. I use explicit casts.
2. Depend on the parser to determine order of operations. I use parentheses (a lot).
3. Use library functions. I'd rather write my own.

And the list goes on... It is a work ethic which may cost me a little more time in front of the keyboard, but my code is much more portable across multiple platforms.

As far as speed and efficiency goes, I guess I'd be using assembler if the speed of initialization affected performance critically.

Bottom line...

If everyone took as much time and effort as you have to insure which linker and compiler options gave you reliable results, this thread, and my advice, would be moot.

Most people (especially those new to programming and in the heat of a robot build) do not. As such, my advise in the future will likely be the same as my advise in the past.

Regards,

WizardOfAz
21-01-2004, 15:41
I guess I'm just too conservative. I have worked with a lot of processors in a lot of environments in multiple languages. I never trust the compiler options in areas like these.

If you have ever spent a week of sleepless nights rewriting code to be explicit rather than implicit because the new compiler (or even version of the compiler) does not compile like the old one did, you would be as gun shy as me.

Other things I do not do:
1. Depend on variable promotion. I use explicit casts.
2. Depend on the parser to determine order of operations. I use parentheses (a lot).
3. Use library functions. I'd rather write my own.

And the list goes on... It is a work ethic which may cost me a little more time in front of the keyboard, but my code is much more portable across multiple platforms.

As far as speed and efficiency goes, I guess I'd be using assembler if the speed of initialization affected performance critically.

Bottom line...

If everyone took as much time and effort as you have to insure which linker and compiler options gave you reliable results, this thread, and my advice, would be moot.

Most people (especially those new to programming and in the heat of a robot build) do not. As such, my advise in the future will likely be the same as my advise in the past.

Regards,

Hi Mike,
Well, nobody that knows me has ever attributed conservitism to me... [;-)

Your discipline is commendable and your advice is good. I didn't mean to infer otherwise. I tend to like to get to know tools intimately and exploit them. But I understand about not becoming dependant on features that are likely to be unavailable in other environments, and that point's well taken.

In this case, I appreciate that the compiler, linker, and runtime guys made the effort to conform to the ANSI standard, and I'll make use of their work.

Best luck to your team, and may you not be haunted by the curse of un-initialized counters and wild pointers.

Bill

Mike Betts
21-01-2004, 16:05
Hi Mike,
Best luck to your team, and may you not be haunted by the curse of un-initialized counters and wild pointers.

Bill,

And may your bit bucket be empty...

Mike

Guest
21-01-2004, 17:19
I guess I'd be using assembler if the speed of initialization affected performance critically.
I'm assuming that writing inline assembly for the robot controller is legal, no?

Mark McLeod
21-01-2004, 17:35
I'm assuming that writing inline assembly for the robot controller is legal, no?

Yes it's legal and profitable too!

Guest
26-01-2004, 22:15
It only assumes constants are 'chars' if they are from 0 to 255. Bigger constants are fine. You can use a quick macro to fix this:

http://nrg.chaosnet.org/repository/viewcode?id=16