Операторы встроенной сборки в коде C и расширенный ASM для архитектур ARM Cortex

Я пытаюсь скомпилировать следующие два фрагмента кода с помощью компилятора ARM 5 для микропроцессора Cortex A:

Часть 1:

static inline void cp15_write_sctlr(uint32_t value)
{
    asm("mcr p15, 0, %0, c1, c0, 0" :: "r"(value));
}

static inline uint32_t cp15_read_actlr(void)
{
    uint32_t actlr;
    asm("mrc p15, 0, %0, c1, c0, 1" : "=r"(actlr));
    return actlr;
}

Часть 2:

static inline void dmb(void)
{
    asm("dmb" ::: "memory");
}

static inline void dsb(void)
{
    asm("dsb" ::: "memory");
}

static inline void isb(void)
{
    asm("isb" ::: "memory");
}

В обоих случаях я получаю ошибки компиляции. См. ниже в качестве примера.

line 64: Error:  #18: expected a ")"
    asm("dsb" ::: "memory");

Ошибка вызвана версией компилятора (компилятор ARM 5), которая не поддерживает Extended Asm?

Если я перепишу код в Часть 1 следующим образом, я не получу никакой ошибки. Эквивалентен ли следующий код коду из части 1?

static inline void cp15_write_sctlr(uint32_t value)
{
    __asm
    {
        MCR p15, 0, value, c1, c0, 0
    }
}

static inline uint32_t cp15_read_actlr(void)
{
    uint32_t actlr;
    __asm
    {
        MRC p15, 0, actlr, c1, c0, 1
    }
    return actlr;
}

Как я мог переписать код в части 2, если компилятор не поддерживает расширенный Asm? Я имею в виду следующее, но не уверен, что это одно и то же.

static inline void dmb(void)
{
    __schedule_barrier();
    __asm("dmb");
    __schedule_barrier();
}

static inline void dsb(void)
{
    __schedule_barrier();
    __asm("dsb");
    __schedule_barrier();
}

static inline void isb(void)
{
    __schedule_barrier();
    __asm("isb");
    __schedule_barrier();
}

Любая помощь могла бы быть полезна.


person Umberto D.    schedule 26.12.2020    source источник
comment
@CraigEstey Сообщение отредактировано. Компилятор - Arm Compiler версии 5, как я писал в посте. Спасибо.   -  person Craig Estey    schedule 27.12.2020
comment
Когда я занимался [коммерческой] разработкой рук, я использовал кросс-компилятор _1_ под Ubuntu. Я никогда раньше не слышал о ARM-компиляторе 5. Я посмотрел, и [AFAICT] это пакет Keil с ребрендингом [за который нужно платить]. Известно, что Кейл немного отстает в некоторых вещах. Вы можете изолировать ассемблерный код в отдельный файл, скомпилировать его с помощью _2_ и загрузить _3_ в IDE. Источник: keil.com/support/man/docs/armcc/armcc_chr1359124246903. htm похоже, что нет расширенного asm.   -  person Umberto D.    schedule 27.12.2020
comment
Или, если вы изолируете ассемблерный код, вы можете поместить его в функции, определенные в файле gcc. Я подозреваю, что с производительностью все будет в порядке [хотя и не так быстро, как чистый встроенный код]. Вы можете добиться того же с помощью функции C, которая на 100% состоит из ассемблерного блока и является чистой. Это может по-прежнему работать как функция gcc. Я бы разобрал файл .o, чтобы увидеть фактический код, сгенерированный для данной функции, чтобы увидеть, насколько хорошо она работает.   -  person Craig Estey    schedule 27.12.2020
comment
Мне нужно проверить возможность использования .s. Однако я вижу, что есть встроенные средства управления барьерами памяти во время копирования: " rel="nofollow noreferrer">ссылка. Просто интересно, будут ли static inline, .o и _4_ вести себя как определения в Части 2.   -  person Craig Estey    schedule 27.12.2020


Ответы (1)


Ошибка вызвана версией компилятора (компилятор ARM 5), которая не поддерживает Extended Asm?

Это встроенный в GNU C синтаксис Extend Asm, так что да, очевидно, что компилятор, который его не поддерживает, выдаст ошибку.


Изменение GCC в 2016 году (PR24414) дал непустым операторам Basic Asm неявное "memory" уничтожение, а также заставить их неявно стирать "cc" на таких целях, как x86, где это делает Extended asm. Поэтому, если ваша версия GCC достаточно новая, вы, я думаю, могли бы безопасно использовать здесь Basic Asm, предполагая, что недокументированное поведение GCC по-прежнему будет присутствовать в будущих версиях GCC, чтобы помочь плохо написанному или старому коду работать. как это, вероятно, было задумано. (Предполагая, что это также безопасно в Keil, с неявным барьером памяти). Я не знаю, в какие версии GCC это превратилось.

Или, что еще лучше, вы могли бы использовать макросы CPP для #if обнаружения плохого компилятора и исключения части ::: "memory", которую он блокирует, при условии, что он рассматривает оператор asm как имеющий засорение памяти. Или определить версии __GNUC__ и __GNUC_MINOR__ и т. д. и использовать их для обнаружения любого компилятора, заявляющего о совместимости с версией диалекта GNU C, которая поддерживает расширенный ассемблер.

Basic Asm никогда не следует использовать внутри функций в GNU C, даже для таких инструкций, как dsb, которые не читают и не записывают регистры, потому что нет документированной гарантии упорядочения по отношению к ним. обращения к памяти, сгенерированные компилятором. https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended. Обычно вы должны использовать его только для тела функции __attribute__((naked)) или в глобальной области видимости.


У Basic asm нет никаких преимуществ; встроенный ассемблер — это то, что вы никогда не должны использовать случайно, и вы ничего не можете сделать с Basic, что вы не можете сделать явно с Extended внутри не-голой функции. (И почти ничего, что вы на самом деле можете сделать безопасно с Basic: вы не можете безопасно трогать регистры или даже глобальные переменные в памяти (см. local-variable-be-used-in-gnu-c-basic-inline-asm-statements/60229384#60229384">this, и не гарантируется, что она будет упорядочена по отношению к чему-либо.)

Так что очень отстойно полагаться на недокументированное поведение Basic Asm, связанное с наличием неявного "memory" clobber. Не было веской причины для этого неявного изменения GCC стирания памяти (за исключением, возможно, того, что старый и / или плохой код с большей вероятностью будет работать правильно); Расширенный ассемблер, чтобы сделать это явным, всегда лучше в GNU C.

Поддерживает ли Keil stdatomic.h для atomic_thread_fence(memory_order_seq_cst) для создания dmb, который нельзя переупорядочить с помощью окружающего кода? (Однако не помогает для dsb и isb).


В чем, конкретно, ваша ошибка? Пожалуйста, отредактируйте свой вопрос и опубликуйте точный текст ошибки в отдельном блоке кода. Какой компилятор вы используете (например, "memory")? И какой ассемблер? Компилятор может поддерживать "cc" и/или asm("" ::: );, но не asm("# comment"); [у них одинаковые возможности].

person Peter Cordes    schedule 27.12.2020
comment
@UmbertoD.: Да, я бы предположил, что внутренние функции предотвращают изменение порядка. обращения к памяти. В противном случае они были бы в основном непригодны для использования, если вы не огородите их вручную, а этого никто не хочет. - person Umberto D.; 27.12.2020
comment
Вы можете использовать мой код лакмусовой бумажки от Godbolt с вашим компилятором Keil вокруг _1_ или чего-то еще, чтобы увидеть, действительно ли он вызывает доступ к памяти до и после. (Хотя на самом деле не нужно было бы блокировать эту оптимизацию для корректности, если она по-прежнему заставляет сохранение происходить до барьера. GCC забывает о значениях всех глобальных переменных — это деталь реализации. Но если это произойдет, вы можете быть уверены что есть неявный барьер памяти компилятора, привязанный к встроенному.) - person Peter Cordes; 27.12.2020
comment
(Лакмусовая бумажка для сохранения и перезагрузки глобального оператора ассемблера покажет вам, как это ведет себя в любой данной версии GCC. Если вы видите, что значение регистра повторно используется для _3_ (расширенный без явного затирания памяти), но не для _4_ (не -empty Basic), that implies non-empty Basic asm statements have an implicit memory clobber. Otherwise you'd expect the same optimization as with Extended. This Godbolt compiler explorer link shows GCC 6.4 does not< /em> имеет неявное удаление памяти для Basic asm, но GCC7. 1 делает. - person Peter Cordes; 27.12.2020