Remove unused function warning for functions for #include files

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

Hi,

for my (small) projects I am declaring all functions static because I believe it makes more optimizations possible (such as inlining the function). In my recent project I use more devices that share some functionality (nRF communication) so I created a .h file and moved common functions to that file. Problem is not all devices use all functions and so I get warning about unused functions (and variables) from the #include file. Is there a way to tell the compiler to not warn about unused stuff from the #include file but still warn about unused stuff from main? Or is it all wrong and I should take another approach?

Thanks

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

Smajdalf wrote:
a .h file and moved common functions to that file.
Not a great plan!!

 

Try reading Dean's article about managing large projects in the Tutorial forum. You put variable and function DECLARATIONs in the .h file you put the DEFINITIONs (the actual implementation) in .c/.cpp files. The use of "static" is to say "this is localised within this single file" so it does not make a lot of sense to put something static in a header.

 

The ONE exception is functions that are "static inline". They may go in a header as it's really a declaration not a definition as it's only at the point of invocation that they are instantiated.

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

Maybe I am wrong but I thought if I create multiple .c files (or libraries) they will be compiled separately - each file seeing only its own functions, oblivious what other files may do with the function and so it cannot make any assumptions. It may be good for larger projects saving resources needed to recompile everything every time. But it is not my case - compiling everything is nearly instant.

OTOH #including a file is the same as copy and paste the content of the source file in place where the #include command is. Which is basically what I was doing manually previously. When I declare a function static the compiler knows it will be used in the local context only - it will not be called from some other (unknown) file and so it is possible to do more optimizations - such as dead code removal.

Why is it not a good plan? Am I wrong and my previous assumptions are not true and there is no gain from declaring functions static and having everything in one file?

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

There should never be anything that actually allocates memory (either function code or variables) in a .h file as the usual use of .h is to share information between multiple compilation units and if the same allocating code is used more than once you end up with multiple allocations of the same named objects.

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

I think I don't understand this. If I for example declare a variable in .h file it will be declared twice only when I include the file twice (or declare a variable with the same name in the original code). But it will only cause an easy to fix compile-time error - where is problem?

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

Have you read the tutorial I referred to in #2 ? Suggest you do.

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

What advantage does your misuse of .h files afford you? Of course, you can do anything you like, but surely you’d want to follow the collective wisdom.

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

I have been trying to picture the situation we are actually talking about here. As far as I can determine OP is talking about something like:

// common.h
static void foo(void) {
    PORTB = 0x55;
}
void bar(void);
// file1.c
#include "common.h"

int main(void) {
    foo();
    bar();
}
// file2.c
#include "common.h"

void bar(void) {
    foo();
}

This will work but you are going to end up with two instances of the foo() code because after preprocessing what the compiler sees is:

// file1.c
static void foo(void) {
    PORTB = 0x55;
}
void bar(void);

int main(void) {
    foo();
    bar();
}
// file2.c
static void foo(void) {
    PORTB = 0x55;
}
void bar(void);

void bar(void) {
    foo();
}

So both files end up being compiled with their own "local" copy for foo(). Because it is "static" neither name is exposed globally but you end up with multiple instances of the same code eating your flash right left and centre.

 

The "traditional" way to share functions is to instantiate (define) them in ONE C file but make that callable code accessible from others. That would be:

// common.h
void foo(void);
void bar(void);
// file1.c
#include "common.h"

int main(void) {
    foo();
    bar();
}
// file2.c
void foo(void) {
    PORTB = 0x55;
}

void bar(void) {
    foo();
}

Now there is just one copy of foo() but both files can call it.

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

BTW just referring back to the OP. Do you still get the warning with -ffunction-sections and -gc-sections? I sort of assume that everyone uses those these days but maybe you have an incomplete build system/makefile?

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

Smajdalf wrote:
I think I don't understand this.

The key concept you seem to be missing is the difference between a declaration and a definition.

 

See: http://c-faq.com/decl/decldef.html and the Tutorial already mentioned by clawson.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your input. I think I have read the tutorial clawson suggested: is it this one?

 

@clawson: I think I want something different. Let say I have 3 devices connected by a nRF24l01 RF link - each having own nRF and controlling ATMega. Two act as Tx and one as Rx to get and log measured data. My idea is one .h file containing declarations of everything related to the nRF communication. A lot will be used by all 3 ATMegas to control the nRF but some functions will be used by the Tx devices only or Rx and one Tx - generating a warning when compiling for the last one. Since each device has its own single .c file, the code duplication you have shown cannot happen.

To terminology: my .h file contains both declarations and definitions. I know "the right way" is to have definitions in separate .c file. But is this differentiating more than just a convention? In other words does the compiler care if the part of code was originally in .h or in .c file?

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

Smajdalf wrote:
does the compiler care if the part of code was originally in .h or in .c file?

The compiler doesn't even know!

 

It is the preprocessor which takes care of #includes, the expanding of #defined macros, and stripping comments.

 

What the compiler sees is the output of the preprocessor.

 

However, as already explained, the trouble with definitions in headers is that it naturally leads to problems with multiple definitions. Which is why the "conventional wisdom" is: don't do that.

 

If you want shared code, put it in a shared .c file - which will probably have an accompanying .h file.

 

You can use conditional compilation to exclude parts of a "shared" source file which are not relevant to a particular build or variant ...

 

EDIT

 

All of this is standard 'C' stuff;  not specific to AVR or Atmel - or even to microcontrollers.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Tue. Jul 10, 2018 - 02:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
You can use conditional compilation to exclude parts of a "shared" source file which are not relevant to a particular build or variant ...
Or, like I say the modern mechanism seems to be to rely on -ffunction-sections (also -fdata-sections) and the link with -gc-sections.

 

In this scenario all functions and all data objects are created but each is assigned to it's own separate memory section. At link time (with -gc-sections where "gc" stand for "garbage collect") the linker tracks the reference count on each memory section. At the end of the link any section that has not been refernced is discarded from the link image. Here is a trivial example:

#include <avr/interrupt.h>

__attribute__((noinline)) void foo() {
    PORTB = 0x55;
}

__attribute__((noinline)) void bar() {
    PORTB = 0xAA;
}

int main(void) {
    while(1) {
        foo();
    }
}
D:\atmel_avr\avr8-gnu-toolchain\bin>avr-gcc -mmcu=atmega16 -Os -g -ffunction-sections -Wl,-gc-sections -Wl,-Map,avr.map avr.c -o avr.elf

D:\atmel_avr\avr8-gnu-toolchain\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr

Disassembly of section .text:

00000000 <__vectors>:

          [SNIP!]

0000006c <foo>:
__attribute__((noinline)) void foo() {
    PORTB = 0x55;
}

__attribute__((noinline)) void bar() {
    PORTB = 0xAA;
  6c:   85 e5           ldi     r24, 0x55       ; 85
  6e:   88 bb           out     0x18, r24       ; 24
  70:   08 95           ret

00000072 <main>:
}

int main(void) {
    while(1) {
        foo();
  72:   0e 94 36 00     call    0x6c    ; 0x6c <foo>
    }
  76:   fd cf           rjmp    .-6             ; 0x72 <main>

00000078 <_exit>:
  78:   f8 94           cli

0000007a <__stop_program>:
  7a:   ff cf           rjmp    .-2             ; 0x7a <__stop_program>
D:\atmel_avr\avr8-gnu-toolchain\bin>head -n 30 avr.map
Archive member included to satisfy reference by file (symbol)

d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
                              d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/../../../../avr/lib/avr5/crtatmega16.o (exit)

Discarded input sections

 .data          0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/../../../../avr/lib/avr5/crtatmega16.o
 .bss           0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/../../../../avr/lib/avr5/crtatmega16.o
 .text          0x00000000        0x0 C:\Users\iAmNotANumber\AppData\Local\Temp\ccMwdbev.o
 .data          0x00000000        0x0 C:\Users\iAmNotANumber\AppData\Local\Temp\ccMwdbev.o
 .bss           0x00000000        0x0 C:\Users\iAmNotANumber\AppData\Local\Temp\ccMwdbev.o
 .text.bar      0x00000000        0x6 C:\Users\iAmNotANumber\AppData\Local\Temp\ccMwdbev.o
 .text          0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .data          0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .bss           0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .text.libgcc.mul
                0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .text.libgcc.div
                0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .text.libgcc   0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .text.libgcc.prologue
                0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .text.libgcc.builtins
                0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .text.libgcc.fmul
                0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)
 .text.libgcc.fixed
                0x00000000        0x0 d:/atmel_avr/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.9.2/avr5\libgcc.a(_exit.o)

In my example main() is accompanied by foo() and bar() but only foo() makes it into the final binary (because it is called from main()). Nothing makes a reference to bar() so it is discarded and listed as a discard in the .map file.

 

I had to apply ((noinline)) to the functions because otherwise foo() got inlined and the "callable" copy was discarded along with bar() which kind of ruined the point I was trying to make.

 

PS for an example of this being used in anger see "LUFA" from Dean Camera. His USB projects basically build everything then it relies in the end on most stuff (that is not used in a particular example) being discarded by -gc-sections

Last Edited: Tue. Jul 10, 2018 - 03:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Smajdalf wrote:
  each device has its own single .c file

What's the benefit of that?

 

The "conventional" approach would be that each device has a specific .c file, and also uses the shared .c files for the common functionality.

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I believe I am still unable to explain what I want:

@awneil: OFC each device has its own .c file. Moreover I have a common .h file included into the .c file. As I understand this preprocessing handles the #include and so compiler sees one large .c file. Naming the common file .c or .h makes no difference - the compiler(*) still remove unused code and warns about declared unused functions. Also I don't want manually disable/enable functions in the included file just to remove the warning. I just hoped for some option to say the compiler(*) "I know there may be some unused functions in this part of code, remove them silently". But I want the warning enabled for functions directly in main.c.

@clawson: I don't know if I am using the -ffunction-sections command. I am using default AS settings but since dead code is removed I think it is applied somehow.

 

(*) I know it is probably not the right term. In AS I click the "Build solution" button and get a binary file. By "compiler" I mean all the stuff between because I don't know responsibilities of all the parts - such as linker, parser etc.

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

Smajdalf wrote:
I believe I am still unable to explain what I want:
Then do as I did in #8 and post a small example of what it is you are actually doing.

 

Also show this actual warning you are seeing about "unused code". I personally have never heard of such a warning from GCC.

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

Example is:

nRF definitions.h(145,13): warning: 'uint8_t updateNRFStatus()' defined but not used [-Wunused-function]
		 static byte updateNRFStatus() {

I don't want this (and then "compiler" knows source of the function is some external file). But if the function comes from main.c I want to be warned.

 

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

That error message already tells you how to suppress this message. It tells you:

[-Wunused-function]

In other words "you are getting this warning because -Wunused-functions has been specified". Well the way to stop that is to recognize that almost all GCC command line flags can be inverted by the inclusion of the word "no-". So add the following to the compiler invocation:

-Wno-unused-function

and it will turn off that specific warning. As it says in the user manual:

 

https://gcc.gnu.org/onlinedocs/g...

You can request many specific warnings with options beginning with ‘-W’, for example -Wimplicit to request warnings on implicit declarations. Each of these specific warning options also has a negative form beginning ‘-Wno-’ to turn off warnings; for example, -Wno-implicit.

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

But when I use -Wno-unused-function I will get NO warnings about unused functions - even for functions in main.c. Is there a way to limit it to the included file?

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

Yeah, compile it separately and apply the change to that alone.

 

Of course to do so will involve you creating a one line .c file to host the single #include of the .h at which point you might as well just switch the .h name to .c and do it like every other C programmer in the world would do it.

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

We are returning back to the beginning: to compile the two files separately the compiler will lose context, functions cannot be static and so some optimizations will me impossible. Possibly high cost for getting rid of a few warnings...

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

Is the potential cost perceived or measured?

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

Perceived.

"Long time" ago I have discovered some functions are compiled better when declared static. Since then I declare all functions static and do not think about it.

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

Don't use static for that reason. Use static for reason that Dennis Ritchie added it to the language! (it's for limiting name scope).

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

And what should I use instead?

Why not to say the compiler scope is limited to the current file when the compiler is not clever enough to know this?

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

Smajdalf wrote:
"Long time" 

Has a high likelihood of being no longer applicable: compilers in general, and their optimisers in particular, have improved a lot over the years.

 

 I declare all functions static and do not think about it.

You really should be thinking about the reasons why you do - and don't do - things.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Smajdalf wrote:
to compile the two files separately the compiler will lose context

suggests that the partitioning is poor.

 

A good partitioning seeks to maximise cohesion within each module, while minimising coupling between modules.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

You really should be thinking about the reasons why you do - and don't do - things.

And you do? So you surely know cost of NOT declaring as many functions as possible static. Do you recalculate it each time you update your tools?

 

awneil wrote:

suggests that the partitioning is poor.

 

A good partitioning seeks to maximise cohesion within each module, while minimising coupling between modules.

 

And? Why should I burden myself with cohesion, coupling and partitioning when I can get everything compiled at once?

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

Smajdalf wrote:
Why should I burden myself with cohesion, coupling and partitioning 

There are only three reasons to do it

  1. Maintainability
  2. Maintainability
  3. Maintainability

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We are slipping quite OT but since noone seems to know the answer to OP I may try to learn something new:

I am still missing your point. I understand you get better maintainability by splitting a long program into more smaller chunks. You propose the chunks should be compiled separately because IF you follow cohesion and coupling the result is NEARLY AS GOOD as if you merge them all into one by #include? I still don't see the motivation to do something that gives me only a bit worse result in optimal conditions. Because since I am a noob and I will have poor cohesion and coupling I will get poor program performance for free? Wow!

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

It is easier to maintain and re-use separate files.
The linker will discard anything that is unused.
You can either disable the Warning or just ignore them.
.
In the old days a linker would link object files (.o) whether referenced or not. But linkers have always been selective when linking object libraries (.a)
Modern linkers are more clever.
.
David.

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

Smajdalf wrote:
you get better maintainability by splitting a long program into more smaller chunks.

Yes, that is the point.

 

As well as improving maintainability, it also improves re-usability; it, makes it easier to take parts of the code from this project for use in another project.

 

You propose the chunks should be compiled separately because (sic) IF you follow cohesion and coupling the result is NEARLY AS GOOD as if you merge them all into one by #include?

No, that is not the reason for it. The reasons are as above.

 

I'm not convinced that it (the "only nearly as good") is even true.

 

I still don't see the motivation to do something that gives me only a bit worse result

It gives significant advantages as noted.

If the performance is still within the requirements, that's a win.

 

If course, if the performance cannot meet the requirements - then something has to be sacrificed.

 

 

EDIT

 

typo & clarification

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Wed. Jul 11, 2018 - 05:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You can build a project by including individual C files into a single project.c file
.
The strategy works. I have used it. But you can get conflicts with static variables. It is unlikely that the Compiler gets any advantage. Your head will hurt.
.
It is wise to follow world convention. Your life will be easier.
.
David.

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

@David: If compiler knows the whole context and knows everything is limited to this context it has more information and so it should generate non-inferior code. Sometimes declaring a function static makes no difference. But sometimes (and not so rarely) it generates code with different size. Sometimes smaller (i.e. recycling some register, dropping unused result) and sometimes larger (likely inlining the function). I was told compiler is clever so I believe those changes are for my good and I try to provide as much information to the compiler as possible. Also I have never encountered naming problem so far - I am using long descriptive names.

@awneil: I understand that for human readability, code reuse, maintaining etc. it is good to divide the source code into small chunks. But that does not mean you have to compile them separately. Those are two very loosely related things. You feed me with reasons why you want to separate the code. I understand it. But why you want to present them to the compiler separately? I can understand it for huge projects maintained by more people. But for 8 bit micro with a few kB of flash?

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

You do know what static does don't you? It says "there doesn't need to be a separately callable version of this function as the name scope is limited". For smallish functions the compiler will often inline the code but, because of global scope, it has to generate a callable copy too. Declaring it "static" says "usage is limited" so it can avoid generating the global. That is why you see space saving. But if you just used function-sections and gc-sections the global versions would be discarded anyway. So you are wasting your time (and making unmanageable code into the bargain). In fact the way you are doing things you'll be generating unnecessary multiple copies of the same thing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>

/*static*/ uint8_t foo() {
  PORTB=5;
  return(PINB);
}

int main(void) {
   foo();
}

Compiles to:

...
/*static*/ uint8_t foo() {
  PORTB=5;
  80:	85 e0       	ldi	r24, 0x05	; 5
  82:	85 b9       	out	0x05, r24	; 5
  return(PINB);
  84:	83 b1       	in	r24, 0x03	; 3
}
  86:	08 95       	ret

00000088 <main>:

int main(void) {
   foo();
  88:	0e 94 40 00 	call	0x80	; 0x80 <_Z3foov>
...

Uncommenting the static:

...
static uint8_t foo() {
  PORTB=5;
  80:	85 e0       	ldi	r24, 0x05	; 5
  82:	85 b9       	out	0x05, r24	; 5
  return(PINB);
  84:	83 b1       	in	r24, 0x03	; 3
}


int main(void) {
  foo();
... 

THIS is one of the reasons I see the space saving.

QED

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

Smajdalf wrote:
THIS is one of the reasons I see the space saving. QED

???  Is this the "spot the difference game"?

 

Well, yeah, in a trivial program with one instance then if code is inlines you see a few words different.

 

But once you use the "standard" pattern of laying out your apps as others have suggested/recommended, and avail yourself of certain compile/link options, I think you might find that the toolchain does a pretty good job, and may in fact match or exceed your hand-crafted solution.

 

 

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

C++ not C then?

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

Oh - yes, I am using C++ (I have no particular reason for this - it is "modern"). I thought it is not so important for this topic. Maybe stupid assumption?

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

Smajdalf wrote:
Maybe stupid assumption?

LOL -- dunno -- but all I need to do is use the "bloat" word...

 

[How did Cliff deduce C++?]

 

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

Yes, the Compiler can do its bast when everything is in one file without any global symbols.
.
Hey-ho. Does it really matter?
Surely it is more important that your App "works" and is easy to maintain.
.
Your time and effort would be best employed by studying world convention.
You can always plough your own furrow if that is what you want. It might be better than the combined wisdom of the world.
.
David.

Last Edited: Wed. Jul 11, 2018 - 09:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

theusch wrote:
[How did Cliff deduce C++?]

My guess is the "_Z3foov" in the listing. That's a C++ mangled name.

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

Image result for by jove holmes, that's brilliant

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

to compile the two files separately the compiler will lose context, functions cannot be static and so some optimizations will me impossible. Possibly high cost for getting rid of a few warnings...

Prove it!

 

 

Example with]

/*static*/ uint8_t foo() {
  PORTB=5;
  return(PINB);
}

 Hmm.   If you call it exactly once and gcc does the automatic inlining, then it's better to have the static. 3 words of program memory instead of 7   (turn on  --relax and it might reduce to 6 words. (hmm. Relax has been removed from the Arduino build process. apparently.))

If you call it more than once, things get ambiguous;  For 3 invocations (if gcc decides to inline them all), you'd have 9 words vs on 6 words for the version that creates call instructions.   If you KNOW that you always want a function inlined, then you can put it in a .h file with "static inline int myfunc(...)"

 

In theory, the relatively new "link time optimization" ("-flto") will delay this sort of inlining decision until later, allowing short functions in separate .c/.cpp files to get optimized the way that you want.

(part of the vast conspiracy to make debugging more difficult!)  I think it does constant-folding at link time too.

 

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

Is there a way to tell the compiler to not warn about unused stuff from the #include file but still warn about unused stuff from main? Or is it all wrong and I should take another approach?

In theory, you can do things like:

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
static int foo() {
    return PORTB;
}
#pragma GCC diagnostic pop

However, this it seems that this particular warning is too "whole program" for it to work (the "it was unused" happens at the end, rather than at the time of definition?)

 

Last Edited: Thu. Jul 12, 2018 - 12:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
In theory, the relatively new "link time optimization" ("-flto")

I'm not a GCC person, but that (and associated) was what I was thinking might help.  I dunno about C++ and what options would be used.  Similar to Codevision "optimize for size" and ImageCraft "code compressor".  Of course they won't make the trivial program be as truncated as OP's posted "example for all to admire" given the nature of complete attention to prologue, as well as attention to what happens when you run off of main().

 

Upon reflection, the posted example doesn't help much.  Of course if the function is only called once from the [trivial] main() and no matter how you get your declaration and definition, it might help to make it static to prevent other modules from "seeing" it.  But I don't see how that relates to OP's premise and making all functions static, nor including .c files as a matter of course.

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

There are two approaches for GCC (apart from the already mentioned -ffunction-sections/-gc-sections) and that would either be LTO (Link Time Optimisation). Both the compiler and linker are invoked with -flto. What then happens is that a lot of the "brain work" that the compiler would usually do is deferred and instead it writes (copious) "internal notes" into the ELF file (.o's) as each source is compiled. The linker then has additional "smarts" (a "plugin") that is invoked at link time that then chews on the internals - effectively moving some of the compiler work but it's now in a place where ALL the parts of the program are visible to it. So it can do "better" optimization than would occur per compilation unit. The other option (and older one but maybe still has merit) is to ivoke the compiler with -whole-program but this also relies on it being given the names of ALL the .c source files at once. It then effectively combines them and builds the thing as if everything had been in one .c file.

 

While it still seems to be "work in progress" LTO is generally considered the "best" option.

 

Of course there is the Johan Ekdahl rule of optimising ;-)