Archive for September, 2007

Code size optimizations

September 28, 2007

During the last two years I have had the opportunity to write embedded software for the SPARC ERC32 architecture. The first piece of software we have written is an application that needs to fit in a 64 Kbytes PROM with a minimal running system that can execute periodic tasks, send packets over a network, load another application stored in an EEPROM that can be remotely patched and many other things. Before starting to write it from scratch we tried to fit RTEMS (a Real-Time Operating System) in the 64 Kbytes. We achieved a running RTEMS in 55 Kbytes, but there was only 10 Kbytes left (note that modern versions of RTEMS are smaller).

The following tips are the ones I have found useful to decrease my application size (they might not work in all architectures). Note that all the tips are C oriented and that gcc has been used.

I would really appreciate if you can share your tricks and I will be glad to include them in the list.

Compress application code

As the application is stored in a PROM, you will normally have a minimal code to perform some initializations and checks (e.g. RAM tests), and the rest of the code for the application itself. The main application will run from RAM, so you could compress the application using a simple compression algorithm such as LZSS, uncompress the application to RAM and run it. The decompression algorithm will be obviously uncompressed.

If you are working with an ERC32 processor check the mkprom-erc32 tool.

Use inline functions judiciously

As it is well-known, performance issues should not be treated while developing. So, writing a lot of inline functions might not have great performance effects, but could have big size penalties if you don’t use them judiciously.

Compiler optimization flags

If performance is not important in your application (i.e. you have no hard real-time requirement), you can use the size optimization options provided by your compiler. For example, in gcc, you can use the -Os option to decrease the executable size. We reduced 5 Kbytes.

Note, that this will avoid inline functions optimization, but you can still tell gcc to include them passing the -finline-functions option.

Split functions into separate files

If you are developing different applications that might share most of the code, you may think on writing an static library. The point is that having a reusable library might affect the executable size. This is because, probably, not all the functions compiled will be used in all the applications. When gcc compiles a source file, all the functions in it will be linked in the final binary even if your code does not use them. Of course, if non of the functions of the source file is being used, they will not be linked. Splitting functions into separate files might solve the problem.

There is one way to avoid this (I haven’t tried it) with gcc using the -ffunction-sections option. This creates a separate section for every function. The linker can then use the –gc-sections option to discard unused sections.

Separate debug and release code

While writing embedded software you may need to have debug and release code. Clear examples are the printing or logging functions that might not be necessary in production code. If this is the case, separate the debug and release code. In C, you could have a serial port printing function in debug and an empty macro in release. This can be achieved via a building system, separating the definitions in different files (which I recommend), or using macros.

Do not use packed structures extensibly

By definition, packed structures are not aligned, this means that, depending on the architecture, the compiler needs to generate extra code to get members of structures. So, using packed structures extensibly may increase your application size and may decrease the performance.

Another reason for not using packed structures is portability.

Enumerations vs. constant objects

As described in this article, constant objects, such as:

unsigned int const ITEM_ID = 0x01200020;

may increase your application size. So, if you really have a few bytes left, you might be interested in changing the definition by using an enumeration:

enum { ITEM_ID = 0x01200020 };

Advertisements

Bomb Practical Common Lisp

September 20, 2007


I started reading PCL some time ago (only time for 10 chapters). This is my first Common Lisp book, and by now I’m not ready to write a great article about Common Lisp details, but if you are looking for a Common Lisp tutorial, this could be one.

Bomb it!

Integer promotion: comparisons

September 16, 2007

This may be something basic, but I lost a bit of time last week trying to find a bug at work, so I thought it was worth mentioning it.

When comparing signed and unsigned expressions of the same size, the compiler produces what it might be unexpected results. Suppose you have this code:

#include <stdio.h>

int
main (void)
{
  unsigned short int a = -12;
  signed short int b = -12;

  printf ("%s\n", (a == b) ? "OK" : "failed");

  return 0;
}

Here, you would probably expect an “OK” output, but surprisingly you get “failed“. Why is this? If you know about integer promotion, you may skip to the end if you don’t want to follow all the process.

Fortunately, I received a copy of K&R last week, so I started digging into the issue to try to understand why this was happening.

About equality operators, K&R section A7.10 reads

The equality operators follow the same rules as the relational operators…

Section A7.9, about relational operator, reads

The usual arithmetic conversions are performed on arithmetic operands…

So, how these arithmetic conversions work?

This is also clearly explained in K&R section A6.5 (the same rules apply to the C99 standard, section 6.3.1.8, Usual arithmetic conversions). The part that interests us is after having evaluated all the real type conversions, when it says

Otherwise, the integer promotions are performed on both operands…

So, before performing the comparison of our operands, both undergo integer promotion. Integer promotion (K&R, section A6.2) says that

If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int.

An int can represent all the values of our operands, so after the integer promotion is performed, both operands have int type. Having a look back to our operands, we know that the a variable holds the value 0xFFF4, and after applying the integer promotion, it maintains the value. The same happens with variable b, that holds 0xFFFFFFF4 to represent -12. Clearly, both values are different and the check fails.

At the end of K&R section A6.5 this is explicitly explained

The new rules are slightly more complicated, but reduce somewhat the suprises that may occur when an unsigned quantity meets signed. Unexpected results may still occur whan an unsigned expression is compared to a signed expression of the same size.

Basically, what’s going on here, is that both variables undergo integer promotion. b is signed, and it is sign-extended. This sign-extension is maintained due to the integer promotion.

Note, that this issue does not occur with 32-bit values, as both operands would be “0xFFFFFFF4”.

So, be careful when comparing unsigned and signed types.