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

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

4 ответа

Решение
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 ( ссылка)

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

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

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

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

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

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

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

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;
}
Другие вопросы по тегам