Implicit declaration of a function uses less memory?

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oddly, I have found that allowing an implicit declaration of a simple function consumes less program memory than having an explicit declaration in my .h file.

foo.h:
void bar(void);

foo.c:
void bar(void)
{
 _delay_ms(500);
}

By commenting out the declaration in my .h, I save 174 bytes of program memory! Anyone know why this might be? I sort of want to implicitly declare much more...

I do have -Os turned on (optimize for size). Using Studio 6.

EDIT: The same thing happens if I move my subroutine to before main().

Very surprising, anyone have an explanation?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you make a small complete test program that demonstrates the symptoms, and post it?

Then the next logical step is to look at the map and listing files of both methods, the better to hunt down that lost chunk of bytes and treat it like the dirty dog it is.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The key thing is where bar() is called. I forget the details but I think that in the absence of a declaration the C compiler assumes:

int function(void) {

and therefore will setup to expect this interface even though it's wrong.

I would have thought this would result in MORE code not less though.

As Lee says, let's see the test program that demonstrates the effect you describe.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sure, here's the example code:

test.c:


#include 
#include 
#include "test.h"

int main(void)
{
    while(1)
    {
        delay500();
    }
}

void delay500(void)
{
	_delay_ms(500);
}

test.h:

void delay500(void);

-Os flag enabled. Compiling as shown consumes 140 bytes. Commenting out the single line in .h (or the #include directive) consumes 124 bytes.

So in this simple example, it's 16 bytes consumed. It's more in my production code (where I'm bumping against the 8k limit of my ATTINY88).

If I implicitly declare the function as returning an int, it's 144 bytes whether or not it's explicitly declared.

I can accept that the optimizer may be a little quirky, but it's an awfully strange thing to use more memory when declaring things as I should.

Last Edited: Mon. Nov 11, 2013 - 10:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With different optimizations:

-O0: 6 bytes consumed when NOT explicitly declaring
-O1: same either way
-O2: explicit declaration consumes 6 bytes
-O3: explicit declaration consumes 10 bytes
-Os: explicit declaration consumes 16 bytes

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

With different optimizations:


But what about the -Om you stated? I'm having a hard time finding that one.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, that was a typo. -Os, optimize for size.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How many object files are you linking? There is a linker option to use rcalls across modules instead of calls, and that will save some flash. Using inline? Not using it will save some flash. Have any strings in ram (not declared const?). Put em in flash. Surprisingly, this might save some flash and WILL save ram.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, jrs454, I need to thank you. You implicitly dragged me kicking and screaming into firing up my newly-loaded Atmel Studio 6.1, and doing my first GCC application ever. [full disclosure: I do some C# work so VisualStudio isn't a complete mystery]

-- Started 6.1
-- "New Project" and poked at selecting a C app and the Tiny88
-- Pasted your code instead of the default "template"
-- Did a build
-- Did another build after correcting to
-- Did another build, commenting out //#include "test.h"
-- Successful build. "Program Memory Usage : 86 bytes 1.0 % Full"

Now, what optimization level did the "wizard" give me? ... Sheesh, I have no idea: "C:\Program Files (x86)\Atmel\Atmel Toolchain\AVR8 GCC\Native\3.4.2.1002\avr8-gnu-toolchain\bin\avr-gcc.exe" -o GccApplication1.elf GccApplication1.o -Wl,-Map="GccApplication1.map" -Wl,--start-group -Wl,-lm -Wl,--end-group -Wl,--gc-sections -mmcu=attiny88

Then I moved dely500() to before main() and rebuilt, with exactly the same size.

[Sheesh--no date/time in any of the output files of a GCC build?]

So I don't get your same size, nor can I reproduce your result.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried the same exercise.

Moving delay500() before main() enables GCC to inline the code directly inside the main() function.

With delay500 in an external file or after main(), it is called as subroutine.

It is pretty obvious really. If avr-gcc knows what is in the function, it can choose whether to inline or not. Size is obviously smaller if you save the RCALL and RET.

Just think how much more accurate the 'delay' will be without the overhead! 82 bytes instead of 86 bytes.

If you want to see what is going on, just run the Simulator in Disassembly view. Be prepared to apply some human intuition to the display. i.e. some of the C lines are not always in the right place.

Forgive me for asking what is the point of this.

If you are tight for flash, there are several things I would try first. After all, avoiding the CALL/RET is only 4 bytes.

Your trivial program is only 20 bytes if you avoid vectors and crts.o functionality.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Well, jrs454, I need to thank you. You implicitly dragged me kicking and screaming into firing up my newly-loaded Atmel Studio 6.1, and doing my first GCC application ever. [full disclosure: I do some C# work so VisualStudio isn't a complete mystery]
...
So I don't get your same size, nor can I reproduce your result.

Glad I could help! :)

I don't think you have any optimizations turned on (or maybe -O1, not sure what is default), so your results are consistent with mine. Try the optimizations to see the change.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:

Forgive me for asking what is the point of this.

If you are tight for flash, there are several things I would try first. After all, avoiding the CALL/RET is only 4 bytes.

Your trivial program is only 20 bytes if you avoid vectors and crts.o functionality.

David.

Thanks for looking into this. It is an odd effect of the optimization, but it's an important one. For whatever reason, in my full code, there was a 174 byte difference. This is not huge but it's far from trivial.

The example I made just shows that the effect exists, even for a small trivial program like that. If it's inlining that's fine, but I'd expect the -Os flag to try to minimize space? But really the only difference is an implicit vs explicit declaration.

It's important because 174 bytes is sometimes important - I noticed it when I properly declared the functions, and ran over the memory limit. In its current state, I can only compile the project if I don't declare these functions as I should.

Certainly there are other places to reduce memory consumption, and as I learn more about developing on this platform I'm learning more of these tricks. If there are any guides to this I'd be happy to read them all.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

If it's inlining that's fine, but I'd expect the -Os flag to try to minimize space?

-fno-inline-small-functions if you want to change compiler behaviour or put an __attribute__((noinline)) on chosen functions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Minimising flash memory is not too difficult.
Mostly by using appropriate sizes of variables and sensible algorithms.

You can always move to a mega168. OTOH, if you want to squeeze 174 bytes from a 8192 byte chip, just offer a price / contract.

Only you know what you might pay / save by squeezing into the mega88.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Try the optimizations to see the change.

Default was -O1, and the size was 86 bytes. Not 170-something. -Os made it 82 bytes, with the function before or after main(). So I cannot reproduce your results.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Not 170-something.
That was in reference to the original code.

The OP's results with the test code you are working with:

jrs454 wrote:
With different optimizations:

-O0: 6 bytes consumed when NOT explicitly declaring
-O1: same either way
-O2: explicit declaration consumes 6 bytes
-O3: explicit declaration consumes 10 bytes
-Os: explicit declaration consumes 16 bytes

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The OP's results with the test code you are working with:
jrs454 wrote:
With different optimizations:

-O0: 6 bytes consumed when NOT explicitly declaring
-O1: same either way
-O2: explicit declaration consumes 6 bytes
-O3: explicit declaration consumes 10 bytes
-Os: explicit declaration consumes 16 bytes


Not my experience, but I didn't explicitly declare it--rather, I moved the function above and below main().

-O1 same either way
-Os same either way, with [the maligned] -Os being 4 bytes smaller.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.