STM8 backend for Clang

Hi there,

Inspired by the recent FOSDEM keynote, I've tried to write an LLVM backend for the STM8 microcontroller platform. The STM8S-Discovery evaluation board [1] has been handed out for free or is sold dirt-cheap, but there is no Open Source cross-compiler for it. The available ST assembler, linker and USB flash programmer are closed-source Windows binaries. And the compiler vendor I contacted for an evaluation license has failed to react at all. So, I was hoping to piece together an STM8 backend for LLVM, for Clang to be able to compile C code into STM8 assembler code that - as a first step - I might be able to use with the ST assembler on some Windows box and transfer it to the board there.

Here's what I've put together so far:
http://repo.or.cz/w/llvm/stm8.git
http://repo.or.cz/w/clang/stm8.git

The first issue I stumbled upon was that the Program Counter register is 24 bits. It seems that despite having been discussed in the past [2], value type i24 was never added. On my branch I did so and, due to ordering assumptions, had to adjust ~30 type enum values in both files [3]. Would such a binary-incompatible change be acceptable for trunk at all?
Further, as pointer width I chose 16 bits since there currently seems to be no way to distinguish between near and far pointers?

Anyway, I've put together enough skeleton code to successfully compile, but on Mac OS X v10.5 ppc host this is what I get:

$ Debug+Asserts/bin/clang -ccc-host-triple stm8-unknown-elf ../hello.c -S -o ../hello.s
0 clang 0x0168c4f0 llvm::SearchForAddressOfSpecialSymbol(char const*) + 448
1 clang 0x0168cc8c llvm::sys::RunInterruptHandlers() + 456
2 libSystem.B.dylib 0x9422d9fc _sigtramp + 68
3 clang 0x0159692c std::_Rb_tree<void const*, std::pair<void const* const, llvm::Pass*>, std::_Select1st<std::pair<void const* const, llvm::Pass*> >, std::less<void const*>, std::allocator<std::pair<void const* const, llvm::Pass*> > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair<void const* const, llvm::Pass*> const&) + 132
4 clang 0x0f1c1cc0 std::_Rb_tree<void const*, std::pair<void const* const, llvm::Pass*>, std::_Select1st<std::pair<void const* const, llvm::Pass*> >, std::less<void const*>, std::allocator<std::pair<void const* const, llvm::Pass*> > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair<void const* const, llvm::Pass*> const&) + 230863896
Stack dump:
0. Program arguments: /Users/andreas/STM8S-Discovery/llvm-stm8/Debug+Asserts/bin/clang -cc1 -triple stm8-unknown-elf -S -disable-free -main-file-name hello.c -mrelocation-model static -mdisable-fp-elim -mconstructor-aliases -target-linker-version 85.2.1 -momit-leaf-frame-pointer -resource-dir /Users/andreas/STM8S-Discovery/llvm-stm8/Debug+Asserts/bin/../lib/clang/2.9 -ferror-limit 19 -fmessage-length 274 -fgnu-runtime -fdiagnostics-show-option -fcolor-diagnostics -o ../hello.s -x c ../hello.c
1. <eof> parser at end of file
clang: error: unable to execute command: Bus error
clang: error: clang frontend command failed due to signal 1 (use -v to see invocation)

Same for stm8-unknown-none. Configured with --enable-targets=host,stm8. (It still works for the default Darwin/ppc target.)

I'm fairly sure there's things I haven't implemented yet but I'm missing an assertion or error message as hint where to continue. Neither printf()s nor gdb breakpoints in the above llvm::SearchForAddressOfSpecialSymbol() are reached.

Any hint what's going wrong or how to debug? Thanks!

Regards,

Andreas

[1] http://www.st.com/stm8s-discovery
[2] http://lists.cs.uiuc.edu/pipermail/llvmdev/2008-November/018663.html
[3] http://repo.or.cz/w/llvm/stm8.git/commitdiff/79de4e74e2bc33894cfef4487be06e4005a1a7a0

Hi there,

Inspired by the recent FOSDEM keynote, I've tried to write an LLVM
backend for the STM8 microcontroller platform. The STM8S-Discovery
evaluation board [1] has been handed out for free or is sold dirt-
cheap, but there is no Open Source cross-compiler for it. The
available ST assembler, linker and USB flash programmer are closed-
source Windows binaries. And the compiler vendor I contacted for an
evaluation license has failed to react at all. So, I was hoping to
piece together an STM8 backend for LLVM, for Clang to be able to
compile C code into STM8 assembler code that - as a first step - I
might be able to use with the ST assembler on some Windows box and
transfer it to the board there.

Here's what I've put together so far:
http://repo.or.cz/w/llvm/stm8.git
http://repo.or.cz/w/clang/stm8.git

The first issue I stumbled upon was that the Program Counter register
is 24 bits. It seems that despite having been discussed in the past
[2], value type i24 was never added. On my branch I did so and, due to
ordering assumptions, had to adjust ~30 type enum values in both files
[3]. Would such a binary-incompatible change be acceptable for trunk
at all?

LLVM doesn't do binary compatibility outside of the representation
used in bitcode files, so that isn't an issue. That said, if the only
24-bit register is the program counter, you could probably get away
with pretending it's a 32-bit register.

Further, as pointer width I chose 16 bits since there currently seems
to be no way to distinguish between near and far pointers?

LLVM doesn't support distinguishing between near and far pointers.
Depending on how much work you want to put in and what exactly you're
trying to build, you might be able to get away with just pretending
all pointers are near and implementing __builtin_stm8_far_loadn() and
__builtin_stm8_far_storen() intrinsics, though.

Anyway, I've put together enough skeleton code to successfully
compile, but on Mac OS X v10.5 ppc host this is what I get:

$ Debug+Asserts/bin/clang -ccc-host-triple stm8-unknown-elf ../hello.c
-S -o ../hello.s
0 clang 0x0168c4f0
llvm::SearchForAddressOfSpecialSymbol(char const*) + 448
1 clang 0x0168cc8c llvm::sys::RunInterruptHandlers() + 456
2 libSystem.B.dylib 0x9422d9fc _sigtramp + 68
3 clang 0x0159692c std::_Rb_tree<void const*,
std::pair<void const* const, llvm::Pass*>,
std::_Select1st<std::pair<void const* const, llvm::Pass*> >,
std::less<void const*>, std::allocator<std::pair<void const* const,
llvm::Pass*> > >::_M_insert(std::_Rb_tree_node_base*,
std::_Rb_tree_node_base*, std::pair<void const* const, llvm::Pass*>
const&) + 132
4 clang 0x0f1c1cc0 std::_Rb_tree<void const*,
std::pair<void const* const, llvm::Pass*>,
std::_Select1st<std::pair<void const* const, llvm::Pass*> >,
std::less<void const*>, std::allocator<std::pair<void const* const,
llvm::Pass*> > >::_M_insert(std::_Rb_tree_node_base*,
std::_Rb_tree_node_base*, std::pair<void const* const, llvm::Pass*>
const&) + 230863896
Stack dump:
0. Program arguments: /Users/andreas/STM8S-Discovery/llvm-stm8/Debug
+Asserts/bin/clang -cc1 -triple stm8-unknown-elf -S -disable-free -
main-file-name hello.c -mrelocation-model static -mdisable-fp-elim -
mconstructor-aliases -target-linker-version 85.2.1 -momit-leaf-frame-
pointer -resource-dir /Users/andreas/STM8S-Discovery/llvm-stm8/Debug
+Asserts/bin/../lib/clang/2.9 -ferror-limit 19 -fmessage-length 274 -
fgnu-runtime -fdiagnostics-show-option -fcolor-diagnostics -o ../
hello.s -x c ../hello.c
1. <eof> parser at end of file
clang: error: unable to execute command: Bus error
clang: error: clang frontend command failed due to signal 1 (use -v to
see invocation)

Same for stm8-unknown-none. Configured with --enable-
targets=host,stm8. (It still works for the default Darwin/ppc target.)

I'm fairly sure there's things I haven't implemented yet but I'm
missing an assertion or error message as hint where to continue.
Neither printf()s nor gdb breakpoints in the above
llvm::SearchForAddressOfSpecialSymbol() are reached.

Any hint what's going wrong or how to debug? Thanks!

Try valgrind? That looks like some sort of memory corruption bug.

-Eli

The example C code uses far pointers, e.g., to set up interrupt vectors:

typedef void @far (*interrupt_handler_t)(void);

struct interrupt_vector
{
   unsigned char interrupt_instruction;
   interrupt_handler_t interrupt_handler;
};

So I do need support for 24-bit pointers in the Clang frontend, in order for the struct to fill four bytes here.
Not sure how intrinsics could help there. I assume that @far is a non-standard keyword. Is it possible to derive a new language standard similar to GCC's --std=gnu99 to handle such extensions? The only guide I could find was on how to add an attribute...

Andreas

You can add extensions that are enabled for only a particular target;
it's just that adding new syntax tends to be a lot of work. Builtins
are a lot easier to implement. :slight_smile:

As for how to do it, just start with parsing and work your way down
the pipeline; there isn't any guide because the specifics tend to vary
a lot with the extension you're implementing.

-Eli

Not being familiar with valgrind, I went on to implement some random parts of the backend and it seems the above error was resolved by overriding the remaining get*Info() methods of LLVMTargetMachine. I've filed a ticket:

http://llvm.org/bugs/show_bug.cgi?id=9334

Andreas