4 bytes less RAM when using my own pointer to FILE instead of stderr

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

I've made a stderr redirection for using with LCD in a standard way:

FILE lcd_output = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);
.
.
.
// main
stderr = &lcd_output;
fprintf_P(stderr, PSTR("fprintf"));

Then I decided to try to declare my own pointer to FILE so I can use another mnemonic instead of stderr:

FILE lcd_output = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);
FILE* stdlcd;
.
.
.
// main
stdlcd = &lcd_output
fprintf_P(stdlcd, PSTR("fprintf"));

and was surprised the latter compiles to 4 bytes of RAM less compared to the first case.

 

Why is that so and is it then better to declare a pointer to FILE than using one of the default stdio streams?

This topic has a solution.

Chupo_cro

Last Edited: Thu. Oct 19, 2017 - 07:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Look at the generated code. I suspect that optimisation is taking place.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

@Chupo_cro said:

Why is that so and is it then better to declare a pointer to FILE than using one of the default stdio streams?

My guess is that when you use stderr, stdin and stdout are also being allocated, even though they are not used.   Two extra pointers requires 4 bytes on an AVR.

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

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

Greg_Muth wrote:
My guess
Good guess:

 

http://svn.savannah.gnu.org/view...

 

37 	FILE *__iob[3];                 /* stdin, stdout, stderr */

stdio.h has:

279 	/**
280 	   Stream that will be used as an input stream by the simplified
281 	   functions that don't take a \c stream argument.
282 	
283 	   The first stream opened with read intent using \c fdevopen()
284 	   will be assigned to \c stdin.
285 	*/
286 	#define stdin (__iob[0])
287 	
288 	/**
289 	   Stream that will be used as an output stream by the simplified
290 	   functions that don't take a \c stream argument.
291 	
292 	   The first stream opened with write intent using \c fdevopen()
293 	   will be assigned to both, \c stdin, and \c stderr.
294 	*/
295 	#define stdout (__iob[1])
296 	
297 	/**
298 	   Stream destined for error output.  Unless specifically assigned,
299 	   identical to \c stdout.
300 	
301 	   If \c stderr should point to another stream, the result of
302 	   another \c fdevopen() must be explicitly assigned to it without
303 	   closing the previous \c stderr (since this would also close
304 	   \c stdout).
305 	*/
306 	#define stderr (__iob[2])

So if you access any one of these you get all 3 as they are an array.

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

clawson wrote:

Greg_Muth wrote:
My guess
Good guess:

 

<snip>

 

So if you access any one of these you get all 3 as they are an array.

Yes, that seems to be the reason. If stdin/stdout was already used for RS232 communication then declaring a new pointer to FILE results in 2 bytes more compared to when using stderr. However, if only one stream is to be used (e.g. only for LCD out), then declaring a new pointer to FILE saves 4 bytes (+2 for a new pointer -6 for the array).

 

Here is the test program where two different outputs can be generated by switching NEW_POINTER from 0 to 1:

#include <avr/io.h>
#include <stdio.h>

#define NEW_POINTER		0	// 0 --> 20 bytes of RAM, 1 --> 16 bytes of RAM

int lcd_putchar(char c, FILE *stream) {
	return 0;
}

FILE  lcd_output = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);
#if NEW_POINTER
	FILE* stdlcd;
#endif

int main(void) {

#if NEW_POINTER
	stdlcd = &lcd_output;
#else
	stderr = &lcd_output;
#endif

#if NEW_POINTER
	fprintf(stdlcd, "");
#else
	fprintf(stderr, "");
#endif

	while(1)
		;
	return 0;
} // main

 

When the switch is 0:

00000092 <.do_clear_bss_loop>:
  92:	1d 92       	st	X+, r1

00000094 <.do_clear_bss_start>:
  94:	a4 31       	cpi	r26, 0x14	; 20
  96:	b1 07       	cpc	r27, r17
  98:	e1 f7       	brne	.-8      	; 0x92 <.do_clear_bss_loop>

 

When the switch is 1:

00000092 <.do_clear_bss_loop>:
  92:	1d 92       	st	X+, r1

00000094 <.do_clear_bss_start>:
  94:	a0 31       	cpi	r26, 0x10	; 16
  96:	b1 07       	cpc	r27, r17
  98:	e1 f7       	brne	.-8      	; 0x92 <.do_clear_bss_loop>

Chupo_cro

Last Edited: Thu. Oct 19, 2017 - 07:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Chupo_cro wrote:
Yes, that seems to be the reason. 

Great.

 

laugh

 

So please mark the solution: http://www.avrfreaks.net/comment...

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

BTW in this case the .map file would have given the answer:

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

int main(void) {
    stdout = (void *)3;
}

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

C:\SysGCC\avr\bin>grep -C 2 __iob avr.map
                              c:/sysgcc/avr/bin/../lib/gcc/avr/5.3.0/../../../../avr/lib/avr5/crtatmega16.o (exit)
c:/sysgcc/avr/bin/../lib/gcc/avr/5.3.0/../../../../avr/lib/avr5\libc.a(iob.o)
                              C:\Users\uid23021\AppData\Local\Temp\cc0vevbF.o (__iob)
c:/sysgcc/avr/bin/../lib/gcc/avr/5.3.0/avr5\libgcc.a(_clear_bss.o)
                              c:/sysgcc/avr/bin/../lib/gcc/avr/5.3.0/../../../../avr/lib/avr5\libc.a(iob.o) (__do_clear_bss)
--
Common symbol       size              file

__iob               0x6               c:/sysgcc/avr/bin/../lib/gcc/avr/5.3.0/../../../../avr/lib/avr5\libc.a(iob.o)

Memory Configuration
--
 *(COMMON)
 COMMON         0x00800060        0x6 c:/sysgcc/avr/bin/../lib/gcc/avr/5.3.0/../../../../avr/lib/avr5\libc.a(iob.o)
                0x00800060                __iob
                0x00800066                PROVIDE (__bss_end, .)
                0x00000092                __data_load_start = LOADADDR (.data)

The use of stdout lead to the allocation of 6 bytes for __iob between 0x800060 and 0x800066

Last Edited: Fri. Oct 20, 2017 - 08:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

Great.

 

laugh

 

So please mark the solution: http://www.avrfreaks.net/comment...

I did mark the solution. I already did that at the time of my last post. But I marked the Greg's post as the solution because he was the first one to give the correct answer. As you can see, Clawson quoted Greg - which can be seen in both Clawson's reply to Greg and my reply to Clawson where there are two levels of quoting. Clawson confirmed Greg was right and presented a proof. I replied to the last post which was Clawson's but marked the first correct answer - which was Greg's.

Chupo_cro

Last Edited: Thu. Oct 26, 2017 - 02:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

BTW in this case the .map file would have given the answer:

 

<snip>

 

The use of stdout lead to the allocation of 6 bytes for __iob between 0x800060 and 0x800066

Thank you very much! Good to know a bettery way. I was examining the .lss file because I am more familiar with assembler than with compiler internals so I thought it would be easier for me to find out what's going on. But clearly, reading the .map file is a better idea.

Chupo_cro