MyVoice home [NL]

Managing code size

Linking and optimization in the GNU toolchain

Writing small code is one thing, another step is linking the code into one program. The linking step is when is decided which code will be part of the program and what will be left out. In order to create a small executable program you need to know the limitations of your tool chain.

Let's have a look at a part of the .map file that is generated as a result of builting the LCD test program:

		  .text 0x400004b4 0x388 i2c.o
             0x400004d4           i2cInit
             0x400007c0           i2cRead
             0x40000540           i2cStart
             0x40000748           i2cPoll
             0x400005b8           i2cStop
             0x400004b4           i2cCheckStatus
             0x4000060c           i2cGetChar
             0x4000057c           i2cRepeatStart
             0x400006b0           i2cWrite
             0x40000688           i2cPutCharAndWait
             0x400005e0           i2cPutChar
             0x40000668           i2cWaitAfterTransmit

This shows that the file i2c.o is linked with all the functions that are in the file, even the i2cRead and i2cGetChar functions that are not used in the program are still linked as part of the process.

Splitting up the i2c code

An easy way to get a smaller program is to make sure that functions are not linked in if these are not used.
Splitting up the i2c.c into separate files is one way to get a smaller code size. I decided to split the module into 4 files: i2c.c contains the general functions that are used by the other functions, i2c_poll.c contains the i2cPoll function (not used that much), i2c_read.c and i2c_write.c the functions related to reading and writing.
I was surprised to discover that the codesize did not change at all! All files were linked together, even the ones with only unused functions. Aparently arm-elf-ld links all .o files it is fed ...

Since the i2c functions are general functions (to control the i2c peripheral device) the files were moved into my DevLib, the device driver library that will contain functions to access the peripheral devices of the LPC21xx family.

This simple design change changes the total code size from 7747 into 7211 bytes and shows it makes sense to think of splitting up your code from the beginning of the project. The build environment from Embedded Artists helps you in this; it is easy to split a larger project into smaller modules and create seperate .a (archive/library)that are linked together.

Removing debug information

Switch off the DEBUG information by setting DEBUG=0 in the makefile does not do a lot, code size now is 7195 bytes.

Thumb interworking support

Thumb interworking also takes up a bit of code, you can switch of the thumb interworking which again saves you a bit of code, the size is now reduced from 7195 to 7043 bytes of code. You should however take care in doing this, when you mix code in THUMB and ARM mode you must specify the -mthumb-interwork flag on compilation.

Using THUMB code

THUMB is a special mode in the ARM processor that uses a 16-bits instruction set, compared to the 32-bits instructions that is normally used. Since this is subset of the normal one, more instructions are needed to perform the same task. ARM has taken a good look into different C-compilers and optimized the thumb set on this making sure that code compiled in THUMB mode is still smaller than in ARM mode.
This reduced code size to just 6563 bytes (compared to 7211 bytes after splitting up the i2c file).

Remember however that every instruction takes one, or more, clockcycle(s) to execute so by using thumb code your program will take more time to complete. Using thumb mode for time critical code sections or e.g. interrupt functions may influence could be the wrong way to optimize your program ...

Here is the code

After all of this you may want to have a look at the changed code and try out to compile things for yourself.