📜 ⬆️ ⬇️

Untranslatable word game about CLANG, LLVM and msp430

The purpose of this work is to obtain the code generated by the back-end compiler LLVM MSP430

Introduction


CLANG is just a front-end C compiler for LLVM. LLVM is a framework for developing front-end, back-end compilers for different target platforms. This framework facilitates the analysis by reducing all front-end to an intermediate representation (IR). All backends operate with this general view.

One of the target platforms that interests me is the MSP430, a popular embedded microcontroller used in many micro-consuming applications.
')
Current support for MSP430 is very limited. I hope I can help someone who is too face-palmit save face. one

Go


Current support for this platform is extremely limited. I hope I can fill this gap. The following is a simplified diagram of how an assembly should occur:

[Source Code (.cpp)] -> [clang] --.ll --> [llc] --.s --> [msp430-gcc] --> elf 


Clang takes your source code and compiles it based on the parameters provided. By default, it hides a lot of things under its shell. All this is nothing more than an attempt to make the process more enjoyable for the developer. In other words, usually you will never look at the intermediate representation of an IR, an assembler, or a link.

What is missing in the documentation for the target MSP430 platform is the fact that clang + llvm will not actually generate an elf binary for you. The code generator simply produces the assembler code for your platform. It is your personal responsibility to link and generate the elf file.

Technically, you don't need the whole msp430-gcc. You just need libc, linker scripts and binutils. On the other hand, msp430-gcc does a useful job, allowing you not to recall all the references and dependencies needed to generate the final elf (vector tables, crt0, ... etc). So use it.

Work environment and initial research


The machine that is used for work is a macbook pro running OSX 10.9.2. Additionally, LLVM and msp430-gcc are used.

Prior to msp430-gcc, I used the patched version of gcc posted on sourceforge.net. Looking for a more recent version, I discovered that it is no longer supported, because Texas Instruments and RHEL are taking it on themselves.

Great, I thought, TI is going to work with RHEL to take over the development, proprietary compilers for windows will no longer be needed.

FACEPALM 1



TI does not recommend using your own solution, use at your own risk (see TI's compiler download). Oke-her ... So what do I hope for?

FACEPALM 2



Well, I can agree that they do not trust their own product, which I feel most of the time. Trying to compile this, I quickly discovered that TI does not support assembly on OS X. This is a consequence of the use of xgcc and the fact that some flags are not supported by OS X.

It should be noted that the work is going on, you just need to remove the extra parameters and add some hacks. I'm lazy, and finishing the existing assembly system was depressing. Well, let's look at other options, once said, so as not to trust this decision.

What I've done


Technically, I don't need a front end, I just need a linker / assembler. I assumed that there is a brew / fink / ports path. I wanted to use the gcc4.7 environment and use brew, but found nothing.

So I created my own fork (based on sampsyo / homebrew-mspgcc). It includes gcc, libc, gdb, mcu, binutils, and mspdebug:
 brew tap wbennett/homebrew-mspgcc brew install wbennett/mspgcc/msp430-binutils brew install wbennett/mspgcc/msp430-gcc brew install wbennett/mspgcc/msp430-libc brew install wbennett/mspgcc/msp430-mcu brew install wbennett/mspgcc/msp430-gdb brew install mspdebug 


Wah ... Now we have gcc.

My face is burning


Everything falls prey to digital decay, but open source software is a source of particular suffering. In particular, the numerous hints presented in the threads of assistance and the degree of relevance to your requests, in relation to the half-life of the information in them provided.

If you are looking for information on the Internet, how to gather in the clang for the msp430 platform, you will not find anything directly. You will find some encouraging sets of parameters, obscene volumes of documentation, and perhaps suggestions to use some options that you did not think about:
 $>clang -ccc-host-triple msp430-elf clang-3.5: error: unsupported option '-ccc-host-triple' clang-3.5: error: no such file or directory: 'msp430-elf' clang-3.5: error: no input files 

Great, it doesn't work.
I can only assume that this is due to the old version of LLVM. Maybe I should have used llc after the clang turned it into LLVM bytecode? .. Well, let's see what options remain.

Let's try a test application:
 #include "msp430.h" #include "stdlib.h" int main(void) { //disable watchdog WDCTL = WDTPW + WDTHOLD; //do something silly auto i = 1; auto result = i+1; //avoid warnings if(result == 2) return 2; //loop forever while(true){} //never reach this return 0; } 


 #compile application to llvm assembler ~/local/bin/clang++ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/ -D__MSP430F5438__ -g -S -emit-llvm -std=c++11 -Wall -c src/simpleadd.cpp -o src/simpleadd.ll 


Fine! Looks like normal LL
 ; ModuleID = 'src/simpleadd.cpp' target datalayout = "em:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.9.0" @"\01__WDTCTL" = external global i32 ; Function Attrs: nounwind ssp uwtable define i32 @main() #0 { %1 = alloca i32, align 4 %i = alloca i32, align 4 %result = alloca i32, align 4 store i32 0, i32* %1 store volatile i32 23168, i32* @"\01__WDTCTL", align 4 store i32 1, i32* %i, align 4 %2 = load i32* %i, align 4 %3 = add nsw i32 %2, 1 store i32 %3, i32* %result, align 4 %4 = load i32* %result, align 4 %5 = icmp eq i32 %4, 2 br i1 %5, label %6, label %7 ; <label>:6 ; preds = %0 ret i32 2 ; <label>:7 ; preds = %0 br label %8 ; <label>:8 ; preds = %7, %8 br label %8 } attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.ident = !{!0} !0 = metadata !{metadata !"clang version 3.5 "} 

Let's translate this into some kind of assembler for msp430, what can llc offer us?
 $>~/local/bin/llc --help ... -mcpu=<cpu-name> - Target a specific cpu type (-mcpu=help for details) #ok more specific plz $>~/local/bin/llc -mcpu=help ... amdfam10 - Select the amdfam10 processor. athlon - Select the athlon processor. athlon-4 - Select the athlon-4 processor. athlon-fx - Select the athlon-fx processor. athlon-mp - Select the athlon-mp processor. athlon-tbird - Select the athlon-tbird processor. athlon-xp - Select the athlon-xp processor. athlon64 - Select the athlon64 processor. athlon64-sse3 - Select the athlon64-sse3 processor. ... 

Why can't we use msp430? I swear I read somewhere that there is a partially supported platform called msp430.

We use the help.
 $>~/local/bin/llc --help ... -march=<string> - Architecture to generate code for (see --version) #ok $>~/local/bin/llc --version LLVM (http://llvm.org/): LLVM version 3.5svn DEBUG build. Built Mar 1 2014 (22:49:35). Default target: x86_64-apple-darwin13.1.0 Host CPU: core-avx-i Registered Targets: aarch64 - AArch64 (ARM 64-bit little endian target) aarch64_be - AArch64 (ARM 64-bit big endian target) arm - ARM cpp - C++ backend hexagon - Hexagon mips - Mips mips64 - Mips64 [experimental] mips64el - Mips64el [experimental] mipsel - Mipsel msp430 - MSP430 [experimental] nvptx - NVIDIA PTX 32-bit nvptx64 - NVIDIA PTX 64-bit ppc32 - PowerPC 32 ppc64 - PowerPC 64 ppc64le - PowerPC 64 LE r600 - AMD GPUs HD2XXX-HD6XXX sparc - Sparc sparcv9 - Sparc V9 systemz - SystemZ thumb - Thumb x86 - 32-bit X86: Pentium-Pro and above x86-64 - 64-bit X86: EM64T and AMD64 xcore - XCore 

Surprisingly, it is supported. Let's generate msp430 assembler!
 ~/local/bin/llc -march=msp430 src/simpleadd.ll -o src/simpleadd.s 

Not sure why he placed main in the text section, but God bless him. 2
  .file "src/simpleadd.ll" .text .globl main .align 2 .type main,@function main: ; @main ; BB#0: push.w r4 mov.w r1, r4 sub.w #12, r1 mov.w #0, -2(r4) mov.w #0, -4(r4) mov.w #0, &__WDTCTL+2 mov.w #23168, &__WDTCTL mov.w #0, -6(r4) mov.w #1, -8(r4) mov.w #0, -10(r4) mov.w #2, -12(r4) mov.w #0, r12 cmp.w #0, r12 jne .LBB0_2 ; BB#1: mov.w #2, r14 mov.w #0, r15 add.w #12, r1 pop.w r4 ret .LBB0_2: ; =>This Inner Loop Header: Depth=1 jmp .LBB0_2 .Ltmp0: .size main, .Ltmp0-main .ident "clang version 3.5 " 

Let's generate a binary.
 $>msp430-gcc -Wall -D_GNU_ASSEMBLER_ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/ -mmcu=msp430f5438 -mcpu=430 -x assembler -Wa,-gstabs -c src/simpleadd.s -o src/simpleadd.o #let's hint our entry point (-e main), because it's weird to be placing main in the text section #let's also generate a map to see how everything is linked $>msp430-gcc -mmcu=msp430f5438 -mcpu=430 -Wl,-Map=a.out.map src/simpleadd.o -o a.out -e main 

Without panic, it seems, we are on the right track.
Unfortunately, facepalm 3. My face is red and bruised. After viewing the map file, we see that the linker does something strange:
 *(.init .init.*) 791 *(.init0) 792 .init0 0x0000000000005c00 0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_re# 793 0x0000000000005c00 _reset_vector__ 794 *(.init1) 795 .init1 0x0000000000005c00 0xc /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__w# 796 0x0000000000005c00 __watchdog_support 797 *(.init2) 798 .init2 0x0000000000005c0c 0x4 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__i# 799 0x0000000000005c0c __init_stack 800 *(.init3) 801 .init3 0x0000000000005c10 0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__l# 802 0x0000000000005c10 __low_level_init 803 *(.init4) 804 .init4 0x0000000000005c10 0x18 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_co# 805 0x0000000000005c10 __do_copy_data 806 .init4 0x0000000000005c28 0x16 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_cl# 807 0x0000000000005c28 __do_clear_bss 808 *(.init5) 809 *(.init6) 810 *(.init7) 811 *(.init8) 812 *(.init9) 813 *(.fini9) 814 .fini9 0x0000000000005c3e 0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__s# 815 0x0000000000005c3e __stop_progExec__ 816 *(.fini8) 817 *(.fini7) 818 *(.fini6) 819 *(.fini5) 820 *(.fini4) 821 *(.fini3) 822 *(.fini2) 823 *(.fini1) 824 *(.fini0) 825 .fini0 0x0000000000005c3e 0x6 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_en# 826 0x0000000000005c3e _endless_loop__ .... 869 .text.crt0 0x0000000000005c48 0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/crt0ivtbl64.o 870 .text 0x0000000000005c48 0x40 src/simpleadd.o 871 0x0000000000005c48 main 

Linker missed the record of switching to main in the .init9 section. Okay, I'll give you a hint.
After inspection of the assembler, it is clear that clang does not understand how the program links, or it doesn’t care:
 ... .file "src/simpleadd.ll" .text .globl main .align 2 .type main,@function main: ; @main ... 

Good Mr.Clang, I think I can help you a little. To indicate where I want the compiler to place the code, I have to use an attribute, namely:
 __attribute__((section(".init9")),aligned(2)). 

The Crt0 library is used to perform startup procedures before moving on to the main program. In addition, it handles the behavior after returning your program from the main function. Since the microcontroller cannot technically “stop”, it will enter an infinite loop after returning from the main.

The steps that will be performed before the main program starts (linked with Crt0) are as follows:
  1. Reset vector
  2. Vochdog system configuration
  3. Stack initialization
  4. Low level initialization
  5. Copying data
  6. RAM cleaning
  7. Call constructors / destructors
  8. Main call
  9. Return processing from main


New code:

 __attribute__((section(".init9"),aligned(2))) int main(void) { ... 

Let's try to collect:

 $>~/local/bin/clang++ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/ -D__MSP430F5438__ -S -emit-llvm -std=c++11 -Wall -c src/simpleadd.cpp -o src/simpleadd.ll ... rc/simpleadd.cpp:4:24: error: argument to 'section' attribute is not valid for this target: mach-o section specifier requires a segment and section separated by a comma __attribute__((section(".init9"),aligned(2))) 

Facepalm 4, Mr. Clang starts to piss me off.
Go back to --help. I read carefully to find where I may have missed something.

In the end, found.
 $>~/local/bin/clang++ --help ... --target=<value> Generate code for the given target ... 

I agree, my cant. I apologize. Well, how can I determine which goal is correct?
Let's try in the manner of previous queries:
 ~/local/bin/clang++ --target=help clang-3.5: error: no input files #version maybe? ~/local/bin/clang++ --version clang version 3.5 Target: x86_64-apple-darwin13.1.0 Thread model: posix 

Unsuccessfully ... And what if you try the obvious:
 ~/local/bin/clang++ -I/usr/local/Cellar/msp430-libc/20120716/msp430/include/ \ -D__MSP430F5438__ -S -emit-llvm -std=c++11 --target=msp430 \ -Wall -c src/simpleadd.cpp -o src/simpleadd.ll 

Babah!

Finally, the collector works as it needs from it, and I can run the program on the device.
Resulting .ll:
 ; ModuleID = 'src/simpleadd.cpp' target datalayout = "em:ep:16:16-i32:16:32-n8:16" target triple = "msp430" @"\01__WDTCTL" = external global i16 ; Function Attrs: nounwind define i16 @main() #0 section ".init9" align 2 { %1 = alloca i16, align 2 %i = alloca i16, align 2 %result = alloca i16, align 2 store i16 0, i16* %1 store volatile i16 23168, i16* @"\01__WDTCTL", align 2 store i16 1, i16* %i, align 2 %2 = load i16* %i, align 2 %3 = add nsw i16 %2, 1 store i16 %3, i16* %result, align 2 %4 = load i16* %result, align 2 %5 = icmp eq i16 %4, 2 br i1 %5, label %6, label %7 ; <label>:6 ; preds = %0 ret i16 2 ; <label>:7 ; preds = %0 br label %8 ; <label>:8 ; preds = %7, %8 br label %8 } attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.ident = !{!0} !0 = metadata !{metadata !"clang version 3.5 "} 

The resulting .s:
  .file "src/simpleadd.ll" .section .init9,"ax",@progbits .globl main .align 1 .type main,@function main: ; @main ; BB#0: push.w r4 mov.w r1, r4 sub.w #6, r1 mov.w #0, -2(r4) mov.w #23168, &__WDTCTL mov.w #1, -4(r4) mov.w #2, -6(r4) mov.w #2, r12 cmp.w #2, r12 jne .LBB0_2 ; BB#1: mov.w #2, r15 add.w #6, r1 pop.w r4 ret .LBB0_2: ; =>This Inner Loop Header: Depth=1 jmp .LBB0_2 .Ltmp0: .size main, .Ltmp0-main .ident "clang version 3.5 " 

Note that main is now correctly located in the resulting .map:
  792 .init0 0x0000000000005c00 0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_re# 793 0x0000000000005c00 _reset_vector__ 794 *(.init1) 795 .init1 0x0000000000005c00 0xc /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__w# 796 0x0000000000005c00 __watchdog_support 797 *(.init2) 798 .init2 0x0000000000005c0c 0x4 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__i# 799 0x0000000000005c0c __init_stack 800 *(.init3) 801 .init3 0x0000000000005c10 0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__l# 802 0x0000000000005c10 __low_level_init 803 *(.init4) 804 .init4 0x0000000000005c10 0x18 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_co# 805 0x0000000000005c10 __do_copy_data 806 .init4 0x0000000000005c28 0x16 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(_cl# 807 0x0000000000005c28 __do_clear_bss 808 *(.init5) 809 *(.init6) 810 *(.init7) 811 *(.init8) 812 *(.init9) 813 .init9 0x0000000000005c3e 0x2c src/simpleadd.o 814 0x0000000000005c3e main 815 *(.fini9) 816 .fini9 0x0000000000005c6a 0x0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a(__s# 817 0x0000000000005c6a __stop_progExec__ 

Now I can continue my work. I hope this helps to reduce the number of facepalm appearing outside this article.

About the author:
William Bennett
I am a senior software engineer at an insurance company. I spend nights working on compilers and runtime managers for embedded systems. I like messing around and collecting things.

Push:
Considering that I mostly use English to read manuals and datasheets, there is a possibility that some moments are not quite correct, so if there are comments, please correct. Personally, I was going to try a bunch of LLVM-mspgcc for a long time, but something always hindered, and here is the article. If everything goes well, I plan to repeat on Ubuntu-msp430-gcc-mspdebug-TI LaunchPad.
Also, in the text there are some points related to the features of OS X, which I also may not know (or rather, I cannot know). So again, refinements and constructive criticism are welcome.

That's all for now, thank you for your attention.

1 - suggested that so more successfully. Perhaps agree.

Source: https://habr.com/ru/post/241812/


All Articles