Подтвердить что ты не робот

Работа __asm__ __volatile__ ( ":" memory")

Что в основном делает __asm__ __volatile__ () и что значение "memory" для архитектуры ARM?

4b9b3361

Ответ 1

asm volatile("" ::: "memory");

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

Например, если вам нужно получить доступ к некоторому адресу в определенном порядке (возможно, потому, что эта область памяти фактически поддерживается другим устройством, а не памятью), вы должны уметь сообщать об этом компилятору, иначе он может просто оптимизировать ваши шаги ради эффективности.

Предположим, что в этом сценарии вы должны увеличивать значение в адресе, читать что-то и увеличивать другое значение в соседнем адресе.

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        d[1] += 1;
        return r;
}

Проблема заключается в компиляторе (gcc в этом случае) может изменить ваш доступ к памяти, чтобы получить лучшую производительность, если вы попросите его (-O). Возможно, это приведет к последовательности инструкций, как показано ниже:

00000000 <c>:
   0:   4603        mov r3, r0
   2:   c805        ldmia   r0, {r0, r2}
   4:   3001        adds    r0, #1
   6:   3201        adds    r2, #1
   8:   6018        str r0, [r3, #0]
   a:   6808        ldr r0, [r1, #0]
   c:   605a        str r2, [r3, #4]
   e:   4770        bx  lr

Вышеуказанные значения для d[0] и d[1] загружаются одновременно. Предположим, что это то, чего вы хотите избежать, тогда вам нужно сказать компилятору, чтобы не изменять порядок доступа к памяти и использовать asm volatile("" ::: "memory").

int c(int *d, int *e) {
        int r;
        d[0] += 1;
        r = e[0];
        asm volatile("" ::: "memory");
        d[1] += 1;
        return r;
}

Итак, вы получите свою последовательность команд, как вы хотите:

00000000 <c>:
   0:   6802        ldr r2, [r0, #0]
   2:   4603        mov r3, r0
   4:   3201        adds    r2, #1
   6:   6002        str r2, [r0, #0]
   8:   6808        ldr r0, [r1, #0]
   a:   685a        ldr r2, [r3, #4]
   c:   3201        adds    r2, #1
   e:   605a        str r2, [r3, #4]
  10:   4770        bx  lr
  12:   bf00        nop

Следует отметить, что это всего лишь компиляция временного барьера памяти, чтобы избежать компилятора для изменения порядка доступа к памяти, поскольку он не добавляет дополнительных инструкций на уровне аппаратного обеспечения для сброса памяти или ожидания загрузки или хранения. ЦП могут по-прежнему изменять порядок доступа к памяти, если у них есть архитектурные возможности, а адреса памяти находятся на normal вместо strongly ordered или device (ref).

Ответ 2

Эта последовательность представляет собой барьер планирования доступа к памяти компилятора, как указано в статье, на которую ссылается Udo. Этот тип специфичен для GCC - другие компиляторы имеют другие способы их описания, некоторые из них содержат более явные (и менее эзотерические) утверждения.

__asm__ является gcc-расширением разрешающих операторов языка ассемблера, вводимых в ваш C-код, используемых здесь для его свойства, чтобы иметь возможность указывать побочные эффекты, которые препятствуют компилятору выполнять определенные типы оптимизаций (которые в этом случай может привести к генерации неправильного кода).

__volatile__ требуется, чтобы оператор asm не переупорядочивался с любым другим изменчивым доступом к любому (гарантия на языке C).

memory - это команда GCC, которая (сорт) говорит, что последовательность inm asm имеет побочные эффекты для глобальной памяти, и, следовательно, необходимо учитывать не только эффекты на локальные переменные.

Ответ 3

Смысл здесь поясняется:

http://en.wikipedia.org/wiki/Memory_ordering

В основном это означает, что код сборки будет выполнен там, где вы ожидаете. Он сообщает компилятору не изменять порядок инструкций. Это то, что закодировано до того, как этот кусок кода будет выполнен раньше и что будет закодировано после того, как будет выполнено после.

Ответ 4

static inline unsigned long arch_local_irq_save(void)
{
    unsigned long flags;

    asm volatile(
        "   mrs %0, cpsr    @ arch_local_irq_save\n"
        "   cpsid   i"      //disabled irq
        : "=r" (flags) : : "memory", "cc");
return flags;
}