Using ELLCC to Build for the Cortex-M3

This weekend I decided to work on getting ELLCC to support the ARM Cortex-M processors. The Cortex-M processors use the Thumb 16 bit instruction set, so adding M support involves adding a configuration file for a member of the Cortex-M family and then building the run-time libraries specifically for the target processor. To start I chose the Cortex-M3. Its configuration file currently looks like this:

[~/ellcc/libecc/config] dev% cat thumb-linux-engeabi
based_on: arm-ellcc-linux
compiler:
options:
- -target arm-ellcc-linux
- -mthumb
- -mcpu=cortex-m3
- -mfpu=none
- -mfloat-abi=softfp
linker:
static_crt1: $R/lib/thumb-linux-engeabi/crt1.o
dynamic_crt1: $R/lib/thumb-linux-engeabi/Scrt1.o
crtbegin: $R/lib/thumb-linux-engeabi/crtbegin.o
crtend: $R/lib/thumb-linux-engeabi/crtend.o
library_paths:
- -L$R/lib/thumb-linux-engeabi

With the config file in place, I attempted to build ELLCC’s standard C library musl for the M. Building a library for a new configuration is pretty easy:

[~/ellcc/libecc] dev% make thumb-linux-engeabi
[~/ellcc/libecc] dev% cd
[~] dev% cd ~/ellcc/libecc
[~/ellcc/libecc] dev% make thumb-linux-engeabi

Well, it isn’t quite that simple. The musl library has several assembly files that are written for the specific target processor. If the assembly language of the desired processor doesn’t match the sources, the sources have to be modified to support the new target. Luckily Thumb instructions are mostly a subset of 32 bit ARM instructions so the changes needed to build musl for the M were fairly minimal.

The next step to support a given processor is to compiler the compiler-rt library. This library contains support functions that handle operations that the processor doesn’t support directly and compiler generated built in functions. In the case of the M processors, this includes things like floating point functions since the M3 doesn’t have a floating point unit. Building compiler-rt was probably the most challenging part of this little project. I had never fully ported the compiler-rt build rules to use the new config files properly so rather than cobbling together something that could handle building for the M, I rewrote the build rules to properly support config files.

The last step is to build the C++ standard libraries, assuming you want C++ support. I ran into one little glitch here. The M doesn’t support 64 bit atomic operations so I had to modify those operations for the M.

Now that the libraries are built, a little experimentation is in order. I decided to try out a simple “hello world”. Here’s the source:

[~/ellcc/examples/hello] dev% cat main.c
#include <stdio.h>

int main()
{
printf("hello world\n");
}

The make files in the example directories can build for multiple targets as long as config files have been set up, so I built for the M:

[~/ellcc/examples/hello] dev% make thumb-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hello'
Compiling main.c
Linking hello
make[1]: Leaving directory `/home/rich/ellcc/examples/hello'
[~/ellcc/examples/hello] dev% ~/ellcc/bin/qemu-arm hello
hello world
[~/ellcc/examples/hello] dev%

Nice! I was curious how the Thumb instruction set affected code size so I did a side by side comparison:

[~/ellcc/examples/hello] dev% size hello
text data bss dec hex filename
19540 156 1412 21108 5274 hello
[~/ellcc/examples/hello] dev% make arm-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hello'
rm -f *.o hello hello.bin hello.log elkconfig.ld
Compiling main.c
Linking hello
make[1]: Leaving directory `/home/rich/ellcc/examples/hello'
[~/ellcc/examples/hello] dev% size hello
text data bss dec hex filename
27432 156 1412 29000 7148 hello

ELLCC creates static binaries by default, so those sizes include all the standard library bits that the program needs, i.e. printf() and software floating point support functions. It is kind of cool that the M is about 15% smaller than the equivalent ARM code for this example.

I decided to try C++. Here’s the code:

[~/ellcc/examples/hellocpp] dev% cat main.cpp
#include <iostream>

int main()
{
std::cout << "hello world" << std::endl; }

Here are the results:

[~/ellcc/examples/hellocpp] dev% make thumb-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hellocpp'
Compiling main.cpp
Linking hellocpp
make[1]: Leaving directory `/home/rich/ellcc/examples/hellocpp'
[~/ellcc/examples/hellocpp] dev% ~/ellcc/bin/qemu-arm hellocpp
hello world

And a size comparison:

[~/ellcc/examples/hellocpp] dev% size hellocpp
text data bss dec hex filename
867540 500 10824 878864 d6910 hellocpp
[~/ellcc/examples/hellocpp] dev% make arm-linux-engeabi
make[1]: Entering directory `/home/rich/ellcc/examples/hellocpp'
rm -f *.o hellocpp hellocpp.bin hellocpp.log elkconfig.ld
Compiling main.cpp
Linking hellocpp
make[1]: Leaving directory `/home/rich/ellcc/examples/hellocpp'
[~/ellcc/examples/hellocpp] dev% size hellocpp
text data bss dec hex filename
1412708 500 10824 1424032 15baa0 hellocpp

Cool stuff.

Finally a size comparison of ELLCC's ecc clang/LLVM based compiler compiled for Thumb and ARM:

[~/ellcc] dev% size bin-thumb-linux-engeabi/ecc
text data bss dec hex filename
37889520 33760 63000 37986280 2439fe8 bin-thumb-linux-engeabi/ecc
[~/ellcc] dev% size bin-arm-linux-engeabi/ecc
text data bss dec hex filename
49504456 33760 63000 49601216 2f4dac0 bin-arm-linux-engeabi/ecc

Looks like the Thumb code is about 25% smaller.

Precompiled binaries supporting the Thumb and all the other ELLCC targets are available in the 0.1.10 release available on the Download page.

3 thoughts on “Using ELLCC to Build for the Cortex-M3

  1. Bernhard Weller

    That’s quite interesting. I’m just wondering why the C++ Hello World program is so huge? Is the whole library in there (no optimizations active)?
    As I’m working on embedded systems I generally avoid using the standard library, especially the print functionality as there is no standard out. So maybe I’m misjudging things and it’s not really that big.
    I should try and see if I can compile one of my own projects with this and see the difference to some other compilers.

    Reply
  2. rich Post author

    Hi Bernhard,

    I agree that statically linked C++ programs seem to be far too large. I’ve heard people say it is caused by the C++ library’s extensive use of templates. I decided to experiment:

    // #include <iostream>
    
    int main()
    {
      // std::cout << "hello world" << std::endl;
    }
    

    After building:

    [~/ellcc/examples/hellocpp] dev% make thumb-linux-engeabi
    make[1]: Entering directory `/home/rich/ellcc/examples/hellocpp'
    Compiling main.cpp
    Linking hellocpp
    make[1]: Leaving directory `/home/rich/ellcc/examples/hellocpp'
    [~/ellcc/examples/hellocpp] dev% size hellocpp
       text    data     bss     dec     hex filename
       2142      16     380    2538     9ea hellocpp
    [~/ellcc/examples/hellocpp] dev%
    

    Now with printf():

    // #include <iostream>
    #include <stdio.h>
    
    int main()
    {
      // std::cout << "hello world" << std::endl;
      printf("hello world\n");
    }
    

    Building again:

    [~/ellcc/examples/hellocpp] dev% make thumb-linux-engeabi
    make[1]: Entering directory `/home/rich/ellcc/examples/hellocpp'
    Compiling main.cpp
    Linking hellocpp
    make[1]: Leaving directory `/home/rich/ellcc/examples/hellocpp'
    [~/ellcc/examples/hellocpp] dev% size hellocpp
       text    data     bss     dec     hex filename
      19708     156    1420   21284 
    

    Back to the original:

    #include <iostream>
    
    int main()
    {
      std::cout << "hello world" << std::endl;
    } 
    

    With the original result:

    [~/ellcc/examples/hellocpp] dev% make thumb-linux-engeabi
    make[1]: Entering directory `/home/rich/ellcc/examples/hellocpp'
    Compiling main.cpp
    Linking hellocpp
    make[1]: Leaving directory `/home/rich/ellcc/examples/hellocpp'
    [~/ellcc/examples/hellocpp] dev% size hellocpp
       text    data     bss     dec     hex filename
     882132     500   10936  893568   da280 hellocpp
    [~/ellcc/examples/hellocpp] dev% 
    

    That seems to be a little insane.

    Reply

Leave a Reply to rich Cancel reply

Your email address will not be published.