Встроенный ассемблерный код ARMv7-M в c с использованием gcc и макросов

У меня есть этот код, который отлично работает:

void function( void )
{
    __asm volatile
    (
        "   ldr r3, .ADDRESS            \n"
        "   mov r2, %0                  \n"
        "   str r2, [r3, %1]            \n"
        ".ADDRESS:  .word 0x401C4000    \n"
        :: "i" (1<<17), "i" (16)
    );
}

Но для объявления .ADDRESS я использовал магическое число 0x401C4000. У меня действительно есть макрос для этого.

Например:

#define ADDR_BASE 0x401C4000
#define ADDR ((void *)ADDR_BASE)

void function( void )
{
    __asm volatile
    (
        "   ldr r3, .ADDRESS            \n"
        "   mov r2, %0                  \n"
        "   str r2, [r3, %1]            \n"
        ".ADDRESS:    .word %2          \n"
        :: "i" (1<<17), "i" (16), "i" (ADDR)
    );
}

Это не строит.

Как я могу использовать макрос в этом случае?

Вы намеревались поместить .word туда, где он будет выполняться как машинный код, сразу после инструкции str? Кроме того, вы знаете, что можете запросить номер или адрес уже в реестре, верно? Если вы используете его только для загрузки или перемещения в регистр, вы просто делаете вещи менее эффективными, не позволяя компилятору потенциально оптимизировать после встраивания этой функции в цикл или что-то в этом роде. Кроме того, вы забыли объявления clobber на R2 и R3 и "memory", если там есть объект C, что было бы ненужным, если бы вы полностью избегали встроенного ассемблера и использовали volatile int*.

Peter Cordes 01.04.2022 11:23

@PeterCordes это вы должны рассматривать только как пример моей проблемы. Предположим, что мне нужно изменить эту функцию github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/portable/GCC/…. Как видите, я не могу написать это на c, а вместо этого мне нужно встроить эти 3 инструкции в пару точек

mastupristi 01.04.2022 11:46

Если у вас есть какие-то инструкции, которые компилятор не может сгенерировать, да, для этого и нужен встроенный ассемблер. Но обычно вы хотите, чтобы Только эти инструкции находились внутри вашего ассемблерного шаблона, сообщая компилятору, что вы хотите в регистрах, прежде чем они запустятся. Ничто не мешает вам сократить это до asm ("str %0, [%2, %1]" :: "r" (1<<17), "i" (16), "r" (ADDR) : "memory"); с ограничениями регистров для двух вещей, которые вам нужны в регистрах, поэтому ваш ассемблерный шаблон не записывает никаких регистров и, следовательно, не нуждается в стирании регистров. И не иметь .word в пути выполнения без .pushsection/.popsection)

Peter Cordes 01.04.2022 22:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

См. правку ниже. Я оставляю это первое решение только в качестве примера того, что вам не следует делать.


Я нашел этот сообщение, где дается решение моей той же проблемы, но оно связано с x86. Я все равно хотел попробовать, и это работает.

Затем используйте %c2 вместо %2, как показано здесь:

#define ADDR_BASE 0x401C4000
#define ADDR ((void *)0x401C4000)

void function( void )
{
    __asm volatile
    (
        "   ldr r3, .ADDRESS            \n"
        "   mov r2, %0                  \n"
        "   str r2, [r3, %1]            \n"
        ".ADDRESS:   .word %c2          \n"
        :: "i" (1<<17), "i" (16), "i" (ADDR): "r2", "r3", "memory"
    );
}

РЕДАКТИРОВАТЬ

Решение, которое я предложил выше, может не работать, видя сгенерированный код:

  20                function:
  21                    @ Function supports interworking.
  22                    @ args = 0, pretend = 0, frame = 0
  23                    @ frame_needed = 1, uses_anonymous_args = 0
  24                    @ link register save eliminated.
  25 0000 04B02DE5      str fp, [sp, #-4]!
  26 0004 00B08DE2      add fp, sp, #0
  27                    .syntax divided
  28                @ 6 "test.c" 1
  29 0008 04309FE5         ldr r3, .ADDRESS            
  30 000c 0228A0E3     mov r2, #131072                  
  31 0010 102083E5     str r2, [r3, #16]            
  32 0014 00401C40  .ADDRESS:   .word 1075593216          
  33                
  34                @ 0 "" 2
  35                    .arm
  36                    .syntax unified
  37 0018 0000A0E1      nop
  38 001c 00D08BE2      add sp, fp, #0
  39                    @ sp needed
  40 0020 04B09DE4      ldr fp, [sp], #4
  41 0024 1EFF2FE1      bx  lr

Вы можете видеть, что в строке 32 символ .ADDRESS был выдан, но он окружен кодом, и нет инструкции его пропустить. Я считаю, что его можно попытаться выполнить, как если бы это была инструкция.

Возможно, лучшее решение, предложенное Питером,

#define ADDR_BASE 0x401C4000
#define ADDR ((void *)0x401C4000)

void function( void )
{
    __asm volatile
    (
    "str %0, [%2, %1]"
    :: "r" (1<<17), "i" (16), "r" (ADDR) : "memory"
    );
}

Поскольку цель этого сайта — архивировать вопросы и ответы, которые будут полезны будущим пользователям, не могли бы вы исправить ошибки в коде, который вы разместили, в частности, отсутствующие clobbers? Эта концепция достаточно сложна для того, чтобы новые программисты поняли ее правильно, не будучи завалены неправильным кодом через Интернет.

Nate Eldredge 01.04.2022 15:55

@NateEldredge Я отредактировал, добавив клобберы. Думаю правильно написал. В ссылке, из которой я взял пример, их не было.

mastupristi 03.04.2022 11:53

Удалил мой отрицательный голос, поскольку единственная оставшаяся ошибка — это демонстрация, где .word находится прямо в разделе .text и выполняется как байты инструкции. Люди не будут копировать это и думать, что это работает, хотя на самом деле это небезопасно; скорее всего просто рухнет. (Если только .word не декодируется как инструкция, которая искажает другие регистры). На сегодняшний день лучшим решением было бы запросить "r"(ADDR) у компилятора вместо того, чтобы материализовать его самостоятельно, тогда вы можете позволить ему выбрать регистр и не нуждаться в уничтожении. И пусть он запланирует загрузку раньше.

Peter Cordes 03.04.2022 13:09

Другие вопросы по теме