, поэтому мы должны использовать выходной операнд = r, чтобы ассемблер автоматически выбирал регистр для нашей переменной. Я прав?
Да, но это компилятор что делает регистр распределения. Он просто заполняет %[operand]
в строке шаблона asm как текстовую подстановку и передает , что ассемблеру.
В качестве альтернативы, вы могли бы жестко кодировать aконкретный регистр в строке шаблона asm и используйте локальную переменную register-asm, чтобы убедиться, что ограничение выбрано "=r"
. Или используйте "=m"
операнд вывода памяти и str
результат в него, и объявите Clobber для любых регистров, которые вы использовали. Но эти альтернативы, очевидно, ужасны по сравнению с простым сообщением компилятору о том, как ваш блок asm может генерировать вывод.
Я не понимаю, почему в комментарии говорится, что оператор return не выполняется:
/* This return will not be reached but is necessary to prevent compiler
warnings. */
return ulOriginalBASEPRI;
Повышение basepri
( ARM docs ) до большего числа может позволить обработчику прерываний запускаться сразу, перед более поздними инструкциями, но если это исключение когда-либо вернется, выполнение будетв конечном итоге достичь C за пределами оператора asm. В этом весь смысл сохранения старого basepri
в регистр и наличия выходного операнда для него, я полагаю.
(я предполагал, что «поднимать» означало большее число = допускается больше прерываний. Но Росскомментирует, что он никогда не допустит больше прерываний, они "поднимают планку" = меньшее число = меньше разрешенных прерываний.)
Если выполнение действительно никогда не закончится, конецваш ассм, вы должны сообщить об этом компилятору . Существует asm goto
, но для этого нужен список возможных целей ветвления. Руководство GCC гласит:
GCC предполагает, что выполнение asm переходит к следующему оператору (если это не так, рассмотрите возможность использования встроенного __builtin_unreachable()
после оператора asm).
Невыполнение этого требования может привести к тому, что компилятор планирует что-то сделать после asm, а затем этого никогда не произойдет, хотя в исходном коде это перед asm.
Itможет быть хорошей идеей использовать "memory"
clobber, чтобы убедиться, что компилятор синхронизирует содержимое памяти с абстрактной машиной C . (По крайней мере, для переменных, отличных от локальных, к которым может обращаться обработчик прерываний). Обычно это желательно в отношении инструкций asm-барьера, таких как dsb
, но, похоже, здесь нам, возможно, не нужно быть барьером памяти SMP, просто о последовательном выполнении после изменения basepri
? Я не понимаю, почему это необходимо, но если вам все-таки стоит подумать о том, является ли переупорядочивание доступа к памяти во время компиляции вокруг оператора asm
проблемой или нет.
You 'Использовать третий разделенный двоеточиями раздел в операторе asm (после входных данных) : "memory"
Без этого компиляторы могут решить сделать присваивание после этого asm вместо до, оставляязначение только в регистрах.
// actual C source
global_var = 1;
uint32_t oldpri = ulPortRaiseBASEPRI();
global_var = 2;
может оптимизировать (посредством устранения мертвых хранилищ) в asm, который работает следующим образом
// possible asm
global_var = 2;
uint32_t oldpri = ulPortRaiseBASEPRI();
// or global_var = 2; here *instead* of before the asm