PSTR(__func__)

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

After reading the GCC docs i do understand why

PSTR(__func__)

does not work, but how can i have function names stored in program memory only?

As i understand it, __FUNCTION__ used to be what i need: a simple string literal.
Unfortunately it is only an alias for __func__ in current GCC versions ...

Thanks,
Bertolt

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

Functions ARE stored in program memory by default? PSTR() is to coerce string literals into code space.

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

Quote:
Functions ARE stored in program memory by default?

But he is looking to store the function name. The question is, why would you want to do that? The function names are used in the C code, not in the output generated by the compiler. So the function names are not stored on the AVR at all.

Regards,
Steve A.

The Board helps those that help themselves.

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

Oh sorry, presumably this is for debug purposes so an assert() or something can output which __function__ and __file__ it was invoked from? I kind of see what he's up to now then.

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

The definition of PSTR() is:

#define PSTR(s) (__extension__({static char __c[] PROGMEM = (s); &__c[0];}))

in which the __extension__ is simply to avoid warnings from the compiler if -pednatic is used. Otherwise it's just creating a PROGMEM array and returning it's address.

So the original post is equivalent to:

{static char __c[] PROGMEM = (__func__); &__c[0];}

When I compile this separately it complains about the initialiser. That's because ("hello") would be a valid string array initialiser but (__func__) would not be as __func__ istelf is a pointer to a string array and not a string.

Cliff

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

clawson wrote:
Oh sorry, presumably this is for debug purposes so an assert() or something can output which __function__ and __file__ it was invoked from? I kind of see what he's up to now then.

Exactly!

void PrintError(PGM_P file, unsigned int line, const char* const function,
                int errNum, PGM_P msg, ...);

#define ERROR(errNum, msg, ...) PrintError(PSTR(__FILE__), __LINE__, __func__,  \                                             
                                           errNum, PSTR(msg), ## __VA_ARGS__)

Program memory strings are realy nice and easy to handle due to the very good support in avr-libc for them!
It is even possible to use them in the var-args part of the error macro because avr-libc can handle them when using "%S" in the format string!

Bertolt

Last Edited: Fri. Sep 21, 2007 - 04:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
The definition of PSTR() is:

#define PSTR(s) (__extension__({static char __c[] PROGMEM = (s); &__c[0];}))

in which the __extension__ is simply to avoid warnings from the compiler if -pednatic is used. Otherwise it's just creating a PROGMEM array and returning it's address.

So the original post is equivalent to:

{static char __c[] PROGMEM = (__func__); &__c[0];}

When I compile this separately it complains about the initialiser. That's because ("hello") would be a valid string array initialiser but (__func__) would not be as __func__ istelf is a pointer to a string array and not a string.

Cliff


Yes, i know and understand that!

What i do not really understand is why __FUNCTION__ was changed from a simple string literal to a variable like thing.

The questing is: Is there a way to tell GCC to revert to the old string literal behaviour?

If not, will there be a fix/workaround for that in a future GCC version? Or maybe even a AVR specific patch?

Bertolt

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

> What i do not really understand is why __FUNCTION__
> was changed from a simple string literal to a variable like thing.

You gotta ask that to the GCC developers. This is most likely the wrong
forum to reach them.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

dl8dtl wrote:
> What i do not really understand is why __FUNCTION__
> was changed from a simple string literal to a variable like thing.

You gotta ask that to the GCC developers. This is most likely the wrong
forum to reach them.


You are right, i will give it a try on the avr-gcc-list.

Bertolt

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

As it's not a particular AVR feature, it would be even better
to go further away, to the GCC developers mailing list.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

bmildner wrote:

 void PrintError(PGM_P file, unsigned int line, const char* const function, int errNum, PGM_P msg, ...); #define ERROR(errNum, msg, ...) PrintError(PSTR(__FILE__), __LINE__, __func__, \ errNum, PSTR(msg), ## __VA_ARGS__) 

Program memory strings are realy nice and easy to handle due to the very good support in avr-libc for them! It is even possible to use them in the var-args part of the error macro because avr-libc can handle them when using "%S" in the format string! Bertolt

 

Do you have the same issue as me that some_print_function(PSTR(__FILE__)); causes a flash memory constant for each instance placed in the code, even within the same compilation unit (i.e. same __FILE__) ? I'm using -O2 and have tried not having separate sections for each data item, -fmerge-constants, but nothing seems to get rid of the multiple instances of the same string in flash.

Last Edited: Thu. May 3, 2018 - 02:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

-fmerge-constants does not work for avr-gcc

 

Your best bet is probably to tokenize your comment words and then have an "unpacker" that dynamically unpacks the common words needed.

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

clawson wrote:

Your best bet is probably to tokenize your comment words and then have an "unpacker" that dynamically unpacks the common words needed.

 

Don't know what you're trying to say here..

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

What I'm saying is that if you have:

char msg1[] = "Hello Fred";
char msg2[] = "Hello Bert";

and you don't want to waste flash with two copies of "Hello" you actually use:

char msg1[] = "£Fred";
char msg2[] = "£Bert";

then later at the point of use you copy the string to be used from flash to RAM but if you encounter '£' then you put "Hello " in the destination string.

 

Obviously the saving only comes in when "Hello " is used multiple times so you want to tokenize the longest substrings possible and also those that are repeated most frequently.

 

You may be able to use just extended character set characters "£éúá..." as you "tokens" as keeping them to single characters but failing that use one as an "escape" so that maybe £ introduces an escaped token then "£H" means use the "Hello " string and so on.

 

Oh and you probably wouldn't do this manually - put all your strings to be tokenised in a separate file then write something in an easy scripting language like Python to auto-tokenize it and regenerate a new source that actually goes into the build.

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

Or just have a list of strings, and the embedded code just uses the indexes - with an external script to look-up the text.

 

That way you don't store the text in the embedded device at all...

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'm purely trying to pool identical strings, I don't care about optimizing identical prefixes/postfixes. The compiler should be able to pool const strings within the same compilation unit even when not copied to ram, but as was said, maybe that doesn't work for avr-gcc, even though is does seem to work when the strings are just inline in the code (ASSERT(__FILE__), etc).

 

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

clawson wrote:
-fmerge-constants does not work for avr-gcc
I am wrong. In the 5.3 version of GCC I have here:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

const char string1[] = "Hello";
const char string2[] = "test";
const char string3[] = "Hello";

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

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -s -j .data avr.elf

avr.elf:     file format elf32-avr

Contents of section .data:
 800060 48656c6c 6f007465 73740048 656c6c6f  Hello.test.Hello
 800070 0000                                 ..

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os -fmerge-all-constants avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -s -j .data avr.elf

avr.elf:     file format elf32-avr

Contents of section .data:
 800060 48656c6c 6f007465 73740000           Hello.test..

So -fmerge-all-constants DOES appear to work just fine.

 

That is for RAM. Adding __flash also gives:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

const __flash char string1[] = "Hello";
const __flash char string2[] = "test";
const __flash char string3[] = "Hello";

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

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os -fmerge-all-constants avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -s -j .text avr.elf

avr.elf:     file format elf32-avr

Contents of section .text:
 0000 0c943000 0c943a00 0c943a00 0c943a00  ..0...:...:...:.
 0010 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0020 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0030 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0040 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0050 0c943a00 48656c6c 6f007465 73740000  ..:.Hello.test..
 0060 11241fbe cfe5d4e0 debfcdbf 0e943c00  .$............<.
 0070 0c943d00 0c940000 ffcff894 ffcf      ..=...........

If I revert to PROGMEM (as one would have to for C++) things don't seem to go quite so well. First using C:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>
#include <avr/pgmspace.h>

const char string1[] PROGMEM = "Hello";
const char string2[] PROGMEM = "test";
const char string3[] PROGMEM = "Hello";

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

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os -fmerge-all-constants avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -s -j .text avr.elf

avr.elf:     file format elf32-avr

Contents of section .text:
 0000 0c943000 0c943a00 0c943a00 0c943a00  ..0...:...:...:.
 0010 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0020 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0030 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0040 0c943a00 0c943a00 0c943a00 0c943a00  ..:...:...:...:.
 0050 0c943a00 48656c6c 6f007465 73740000  ..:.Hello.test..
 0060 11241fbe cfe5d4e0 debfcdbf 0e943c00  .$............<.
 0070 0c943d00 0c940000 ffcff894 ffcf      ..=...........

But now the equivalent for C++

C:\SysGCC\avr\bin>type avr.cpp
#include <avr/io.h>
#include <avr/pgmspace.h>

const char string1[] PROGMEM = "Hello";
const char string2[] PROGMEM = "test";
const char string3[] PROGMEM = "Hello";

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

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os -fmerge-all-constants avr.cpp -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -s -j .text avr.elf

avr.elf:     file format elf32-avr

Contents of section .text:
 0000 0c942a00 0c943400 0c943400 0c943400  ..*...4...4...4.
 0010 0c943400 0c943400 0c943400 0c943400  ..4...4...4...4.
 0020 0c943400 0c943400 0c943400 0c943400  ..4...4...4...4.
 0030 0c943400 0c943400 0c943400 0c943400  ..4...4...4...4.
 0040 0c943400 0c943400 0c943400 0c943400  ..4...4...4...4.
 0050 0c943400 11241fbe cfe5d4e0 debfcdbf  ..4..$..........
 0060 0e943600 0c943700 0c940000 ffcff894  ..6...7.........
 0070 ffcf                                 ..

I looked in every (-s) section of the objdump and it seems that for C++ and -fmerge-all-constants that it has simply "eaten" the strings - there's no sign of them in the ELF ??

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

Yes, I also saw somewhere else that on avr-gcc 6.? using -fno-merge-constants circumvented some bug, which implies that merge-constants is not completely without effect.

You should be careful when using merge-all-constants, by the way:

-fmerge-all-constants

Attempt to merge identical constants and identical variables.

This option implies -fmerge-constants. In addition to -fmerge-constants this considers e.g. even constant initialized arrays or initialized constant variables with integral or floating-point types. Languages like C or C++ require each variable, including multiple instances of the same variable in recursive calls, to have distinct locations, so using this option results in non-conforming behavior.

 Edit:

Apparently -merge-constants will make no difference for me, as it is for merging constants *across* compilation units, which implies that within a compilation unit, merging should occur regardless of if merge-constants is on or not:

-fmerge-constants

-fmerge-constants

Attempt to merge identical constants (string constants and floating-point constants) across compilation units.

This option is the default for optimized compilation if the assembler and linker support it. Use -fno-merge-constants to inhibit this behavior.

Enabled at levels -O, -O2, -O3, -Os.

Last Edited: Fri. May 4, 2018 - 09:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was under the impression that C and C++ were allowed to merge

anonymous strings, but not necessarily named global strings;

that different named objects are supposed to have different addresses. 

Iluvatar is the better part of Valar.