Что делает ASM volatile в C?
Я просмотрел некоторый код C из
http://www.mcs.anl.gov / ~kazutomo/rdtsc.html
Они используют такие вещи, как "inline","asm " etc как следующее:
code1:
static __inline__ tick gettick (void) {
unsigned a, d;
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) );
return (((tick)a) | (((tick)d) << 32));
}
code2:
volatile int __attribute__((noinline)) foo2 (int a0, int a1) {
__asm__ __volatile__ ("");
}
мне было интересно, что делают code1 и code2?
3 ответов
на __volatile__
модификатор на __asm__
блок заставляет оптимизатор компилятора выполнять код как есть. Без него оптимизатор может подумать, что его можно либо удалить сразу, либо вытащить из цикла и кэшировать.
это полезно для rdtsc
инструкция Вот так:
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )
это не требует зависимостей, поэтому компилятор может предположить, что значение может быть кэшировано. Volatile используется, чтобы заставить его читать новую метку времени.
при использовании в одиночку, вот так:
__asm__ __volatile__ ("")
он фактически ничего не будет выполнять. Однако вы можете расширить это, чтобы получить барьер памяти во время компиляции, который не позволит переупорядочить инструкции по доступу к памяти:
__asm__ __volatile__ ("":::"memory")
на rdtsc
инструкция является хорошим примером для volatile. rdtsc
обычно используется, когда вам нужно время, сколько времени требуется для выполнения некоторых инструкций. Представьте себе такой код, где вы хотите время r1
и r2
'ы исполнение:
__asm__ ("rdtsc": "=a" (a0), "=d" (d0) )
r1 = x1 + y1;
__asm__ ("rdtsc": "=a" (a1), "=d" (d1) )
r2 = x2 + y2;
__asm__ ("rdtsc": "=a" (a2), "=d" (d2) )
здесь компилятору фактически разрешено кэшировать метку времени, и допустимый вывод может показать, что каждая строка заняла ровно 0 часов для выполнения. Очевидно, это не то, что вы хотите, поэтому вы вводите __volatile__
чтобы предотвратить кэширование:
__asm__ __volatile__("rdtsc": "=a" (a0), "=d" (d0))
r1 = x1 + y1;
__asm__ __volatile__("rdtsc": "=a" (a1), "=d" (d1))
r2 = x2 + y2;
__asm__ __volatile__("rdtsc": "=a" (a2), "=d" (d2))
теперь вы каждый раз будете получать новую метку времени, но у нее все еще есть проблема, что компилятору и процессору разрешено переупорядочивать все эти операторы. Это может привести к выполнению блоков asm после r1 и r2 уже подсчитано. Чтобы обойти это, вы бы добавили некоторые барьеры, которые заставляют сериализацию:
__asm__ __volatile__("mfence;rdtsc": "=a" (a0), "=d" (d0) :: "memory")
r1 = x1 + y1;
__asm__ __volatile__("mfence;rdtsc": "=a" (a1), "=d" (d1) :: "memory")
r2 = x2 + y2;
__asm__ __volatile__("mfence;rdtsc": "=a" (a2), "=d" (d2) :: "memory")
Примечание mfence
инструкция Здесь, которая обеспечивает барьер на стороне процессора, и спецификатор" память " в блоке volatile, который обеспечивает барьер времени компиляции. На современных процессорах вы можете заменить mfence:rdtsc
С rdtscp
для чего-то более эффективного.
asm
предназначен для включения собственного кода сборки в исходный код C. Е. Г.
int a = 2;
asm("mov a, 3");
printf("%i", a); // will print 3
компиляторы имеют разные варианты. __asm__
должно быть синонимом, возможно, с некоторыми отличиями от компилятора.
volatile
означает, что переменная может быть изменена извне (ака не программой C). Например, при программировании микроконтроллера, где адрес памяти 0x0000x1234
сопоставляется с некоторым интерфейсом устройства (т. е. при кодировании для Геймбой, кнопки/экран/и т. д. доступны таким образом.)
volatile std::uint8_t* const button1 = 0x00001111;
это отключенные оптимизации компилятора, которые полагаются на *button1
не изменяется, если не изменяется кодом.
он также используется в многопоточном программировании (больше не нужен сегодня?), где переменная может быть изменена другим потоком.
inline
- это подсказка компилятору для "встроенных" вызовов функции.
inline int f(int a) {
return a + 1
}
int a;
int b = f(a);
это не должно быть скомпилировано в вызов функции к f
а в int b = a + 1
. Как будто f
где макроса. Компиляторы в основном делают эту оптимизацию автоматически в зависимости от использования функции/содержимого. __inline__
в этом примере может иметь более конкретный смысл.
аналогично __attribute__((noinline))
(специфичный для GCC синтаксис) предотвращает встроенную функцию.
на __asm__
атрибут задает имя, которое будет использоваться в коде ассемблера для функции или переменной.
на __volatile__
квалификатор, обычно используемый в вычислениях в реальном времени встроенных систем, решает проблему с тестами компилятора status register
на ERROR
или READY
бит, вызывающий проблемы при оптимизации. __volatile__
был введен как способ сообщить компилятору, что объект подвержен быстрым изменениям и заставить каждую ссылку объекта быть подлинной ссылка.