[Перевод] Непереводимая игра слов по поводу CLANG, LLVM и msp430

Целью данной работы является получение кода, сгенерированного бэк-эндом компилятора LLVM MSP430 CLANG — это просто фронт-энд компилятора С для LLVM. LLVM — это фреймворк для разработки фронт-эндов, бэк-эндов компиляторов для разных целевых платформ. Этот фреймворк облегчает анализ путем приведения всех фронт-эндов к промежуточному представлению (IR). Все бэк-энды оперируют с этим общим представлением.Одна из целевых платформ, которая меня интересует, это MSP430 — популярный встраиваемый микроконтроллер, используемый во многих микропотребляющих приложениях.

Текущая поддержка MSP430 весьма ограничена. Я надеюсь, что смогу помочь кому-нибудь, упорно фейс-палмя, чтобы сохранить их лицо.

Текущая поддержка данной платформы предельно ограничена. Надеюсь, я смогу восполнить этот пробел. Ниже приведена упрощенная диаграмма того, как должна происходить сборка: [Source Code (.cpp)] → [clang] --.ll --> [llc] --.s --> [msp430-gcc] --> elf Clang берет ваш исходный код и компилирует его, основываясь на предоставленных параметрах. По умолчанию он скрывает много чего под своей оболочкой. Все это не что иное, как попытка сделать процесс более приятным для разработчика. Другими словами, обычно вы никогда не заглянете в промежуточное представление IR, ассемблер или линковку.

Что упущено в документации по целевой платформе MSP430, так это факт, что clang + llvm на самом деле не будет генерировать для вас бинарник elf. Кодогенератор просто производит ассемблерный код для вашей платформы. Это ваша личная обязанность произвести линковку и генерацию файла elf.

Технически, вам не нужен весь msp430-gcc. Вам всего лишь нужны libc, скрипты линкера и binutils. С другой стороны, msp430-gcc делает полезное дело, позволяя вам не вспоминать все ссылки и зависимости, необходимые для генерации финального elf (vector tables, crt0,…etc). Так что воспользуемся им.

Машина, которая используется для работы — это macbook pro под управлением OSX 10.9.2. Дополнительно используется LLVM и msp430-gcc.До msp430-gcc я использовал патченную версию gcc, выложенную на sourceforge.net. В поисках более свежей версии я обнаружил, что она больше не поддерживается, потому что Texas Instruments и RHEL берут это на себя.

Здорово, подумал я, TI собирается работать с RHEL, чтобы взять на себя разработку, больше не нужны будут проприетарные компиляторы под windows.

TI не рекомендует к использованию собственное решение, используйте на свой страх и риск (see TI’s compiler download). Oке-ей… Так на что я надеюсь?

Хорошо, я могу согласиться, что они не доверяют собственному продукту, что и я чувствую большую часть времени. Пытаясь собрать это, я быстро обнаружил, что TI не поддерживает сборку на OS X. Это следствие использования xgcc и того факта, что некоторые флаги не поддерживаются OS X.

Следует заметить, что работа идет, нужно лишь убрать лишние параметры и добавить некоторые хаки. Я ленив, и допиливание существующей системы сборки наводило тоску. Что ж, давайте посмотрим другие варианты, раз уж сказали, чтобы не доверять данному решению.

Технически мне не нужен фронт-энд, мне всего лишь нужен линкер/ассемблер. Я предполагал, что есть brew/fink/ports путь. Я хотел использовать окружение gcc4.7 и использовать brew, но ничего не нашел.Так что я создал свой собственный форк (основанный на sampsyo/homebrew-mspgcc). Он включает gcc, libc, gdb, mcu, binutils и 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 Вах… Теперь у нас есть gcc.

Все становится жертвой цифрового распада, но особенное страдание доставляет open source софт. В частности, многочисленные хинты, представленные в тредах помощи и степень релевантности вашим запросам, по отношению к периоду полураспада информации, в них предоставленной.Если вы ищете в интернетах информацию, как собрать в clang для платформы msp430, напрямую вы ничего не найдете. Вы найдете немного обнадеживающие наборы параметров, непотребные объемы документации и возможно, предложения использовать некоторые опции, о которых вы не думали:

$>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 Отлично, это не работает.Я могу только предположить, что это из-за старой версии LLVM. Может быть, я должен был использовать llc после того, как clang превратил его в LLVM байткод?… Хорошо, посмотрим, какие варианты ещё остались.Попробуем тестовое приложение:

#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 Отлично! Выглядит, как нормальный LL

; ModuleID = 'src/simpleadd.cpp' target datalayout = «e-m: 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

;

;

;

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 »} Давайте уже переведем это в какой-нибудь ассемблер для msp430, что llc может нам предложить? $>~/local/bin/llc --help … -mcpu= — 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. … Почему мы не можем использовать msp430? Я клянусь, что где-то читал, что существует частично поддерживаемая платформа, называемая msp430.Воспользуемся помощью.

$>~/local/bin/llc --help … -march= — 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 Удивительно, но он поддерживается. Давайте сгенерируем msp430 ассемблер! ~/local/bin/llc -march=msp430 src/simpleadd.ll -o src/simpleadd.s Не уверен, почему он разместил main в секции text, ну да бог с ним. .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 » Давайте сгененируем бинарник. $>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 Без паники, похоже, мы на верном пути.К сожалению, facepalm 3. Мое лицо красное и в синяках. После просмотра map файла видим, что линкер делает нечто странное: *(.init .init.*) 791 *(.init0) 792 .init0 0×0000000000005c00 0×0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (_re# 793 0×0000000000005c00 _reset_vector__ 794 *(.init1) 795 .init1 0×0000000000005c00 0xc /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__w# 796 0×0000000000005c00 __watchdog_support 797 *(.init2) 798 .init2 0×0000000000005c0c 0×4 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__i# 799 0×0000000000005c0c __init_stack 800 *(.init3) 801 .init3 0×0000000000005c10 0×0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__l# 802 0×0000000000005c10 __low_level_init 803 *(.init4) 804 .init4 0×0000000000005c10 0×18 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (_co# 805 0×0000000000005c10 __do_copy_data 806 .init4 0×0000000000005c28 0×16 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (_cl# 807 0×0000000000005c28 __do_clear_bss 808 *(.init5) 809 *(.init6) 810 *(.init7) 811 *(.init8) 812 *(.init9) 813 *(.fini9) 814 .fini9 0×0000000000005c3e 0×0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__s# 815 0×0000000000005c3e __stop_progExec__ 816 *(.fini8) 817 *(.fini7) 818 *(.fini6) 819 *(.fini5) 820 *(.fini4) 821 *(.fini3) 822 *(.fini2) 823 *(.fini1) 824 *(.fini0) 825 .fini0 0×0000000000005c3e 0×6 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (_en# 826 0×0000000000005c3e _endless_loop__ … 869 .text.crt0 0×0000000000005c48 0×0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/crt0ivtbl64.o 870 .text 0×0000000000005c48 0×40 src/simpleadd.o 871 0×0000000000005c48 main Линкер пропустил запись о переходе на main в секции .init9. Ладно, дам подсказку.После инспекции ассемблера видно, что clang не понимает, как линкуется программа, или ему все равно: … .file «src/simpleadd.ll» .text .globl main .align 2 .type main,@function main: ; @main … Хорошо Mr.Clang, думаю, я могу вам немного помочь. Чтобы указать, где я хочу, чтобы компилятор поместить код, я должен использовать атрибут, а именно: __attribute__((section (».init9»)), aligned (2)). Библиотека Crt0 используется для выполнения процедур стартапа, прежде чем переходить к основной программе. Кроме того, она обрабатывает поведение после возврата вашей программы из функции main. Поскольку микроконтроллер не может технически «остановиться», он войдет в бесконечный цикл после возвращения из main.Шаги, которые будут выполняться до того, как запустится основная программа (линкованная с Crt0), следующие:

Вектор сброса Конфигурация системы вочдога Инициализация стека Низкоуровневая инициализация Копирование данных Очистка ОЗУ Вызов конструкторов/деструкторов Вызов main Обработка возврата из main Новый код:

__attribute__((section (».init9»), aligned (2))) int main (void) { … Попробуем собрать: $>~/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 начинает выводить меня из себя.Возвращаемся к --help. Я внимательно читаю, чтобы найти, где я, возможно, что-то упустил.В конце концов, нашел.

$>~/local/bin/clang++ --help … --target= Generate code for the given target … Согласен, мой косяк. Прошу прощения. Ну и как мне определить, какая цель правильная? Попробуем по образу предыдущих запросов: ~/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 Неудачно… А что, если попробовать очевидное: ~/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 Бабах! Наконец-то сборщик работает как от него и требуется, и я могу запустить программу на устройстве.Результирующий .ll:

; ModuleID = 'src/simpleadd.cpp' target datalayout = «e-m: e-p: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

;

;

;

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 »} Результирующий .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 » Обратите внимание, что main теперь правильно размещен в результирующем .map: 792 .init0 0×0000000000005c00 0×0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (_re# 793 0×0000000000005c00 _reset_vector__ 794 *(.init1) 795 .init1 0×0000000000005c00 0xc /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__w# 796 0×0000000000005c00 __watchdog_support 797 *(.init2) 798 .init2 0×0000000000005c0c 0×4 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__i# 799 0×0000000000005c0c __init_stack 800 *(.init3) 801 .init3 0×0000000000005c10 0×0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__l# 802 0×0000000000005c10 __low_level_init 803 *(.init4) 804 .init4 0×0000000000005c10 0×18 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (_co# 805 0×0000000000005c10 __do_copy_data 806 .init4 0×0000000000005c28 0×16 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (_cl# 807 0×0000000000005c28 __do_clear_bss 808 *(.init5) 809 *(.init6) 810 *(.init7) 811 *(.init8) 812 *(.init9) 813 .init9 0×0000000000005c3e 0×2c src/simpleadd.o 814 0×0000000000005c3e main 815 *(.fini9) 816 .fini9 0×0000000000005c6a 0×0 /usr/local/Cellar/msp430-gcc/4.7.0/lib/gcc/msp430/4.7.0/mmpy-16/libcrt0.a (__s# 817 0×0000000000005c6a __stop_progExec__ Теперь я могу продолжить свою работу. Надеюсь, это поможет уменьшить количество фейспалмов, проявившихся за пределами данной статьи.Об авторе: Вильям БеннеттЯ старший инженер-программист в страховой компании. Я провожу ночи, работая над компиляторами и менеджерами рантайма для встраиваемых систем. Мне нравится возиться и собирать разные вещи.

От себя: Учитывая, что английским я в основном пользуюсь для чтения мануалов и даташитов, есть вероятность не вполне правильного перевода некоторых моментов, так что если есть замечания, прошу поправить. Лично я давно собирался попробовать именно связку LLVM-mspgcc, но что-то всегда мешало, а тут эта статья. Если все пойдет хорошо, планирую повторить на Ubuntu-msp430-gcc-mspdebug-TI LaunchPad.Также, в тексте есть некоторые моменты, связанные с особенностями OS X, которые я тоже могу не знать (вернее, не могу знать). Так что опять же, уточнения и конструктивная критика приветствуются.

На этом пока все, спасибо за ваше внимание.

© Habrahabr.ru