STM32F4 не выталкивает/выталкивает регистры с плавающей запятой

у меня есть код

const float previousTemperature = getTemperature();
someNestedFunction();
someOtherActions();
setTemperature(previousTemperature);

Моя проблема в том, что в someNestedFunction(); previousTemperature модифицируется. Я просмотрел дизассемблирование и не могу найти никаких инструкций, которые перед вызовом выталкивали бы или извлекали бы регистр, в котором хранится previousTemperature.

Я проверил документацию ARM и там написано:

Остальные регистры в FPU, то есть S0-S15 и FPSCR, всегда сохраняются автоматически.

Моя переменная хранится в S18, так как я могу убедиться, что она будет правильно сохранена и восстановлена? Я хотел бы найти решение, которое позволило бы избежать возни с Asm.

Мой процессор STM32F437ZGT, и я использую компилятор Keil uVision 5.

ИЗМЕНИТЬ:

До сих пор я пытался прокомментировать содержимое someNestedFunction(), чтобы узнать, что именно изменяет регистр, в котором находится моя переменная. Оказывается, его задел звонок

snprintf(msg, 10, "%4.2f", value/100.0f);

Завтра проверю, не портит ли что-нибудь стек.

Изменить2:

Регистр, в котором находился previousTemperature, все-таки был вытолкнут/вытолкнут. Я этого не заметил, т.к. в разборках использовался регистр D9, который затеняет S18 и S19. Кроме того, кажется, что value в snprintf(msg, 10, "%4.2f", value/100.0f); был каким-то неинициализированным мусором. После исправления previousTemperature все время остается одним и тем же. Однако, почему это происходит, для меня загадка, и я боюсь, что истинная проблема все еще там, просто я не могу наблюдать никаких симптомов.


person mactro    schedule 31.08.2016    source источник
comment
previosTemperature — это не то же имя переменной, что и константа: обратите внимание на опечатку: (предыдущие против предыдущих). Я предполагаю, что это просто ошибка при копировании вашего кода в вопрос. Если вы не используете какой-либо встроенный ассемблер, то это звучит как ошибка компилятора, если вы каким-то образом не лжете компилятору.   -  person Peter Cordes    schedule 31.08.2016
comment
@PeterCordes да, это была опечатка. Я нигде не использую встроенный ассемблер.   -  person mactro    schedule 31.08.2016
comment
Если ему нужно пережить вызов функции, компилятор должен поместить его в регистр, который, по словам ABI, сохраняется при вызове. Я не пользовался инструментами Keil, но ненормально делать что-то особенное, чтобы простая семантика C (например, переменные сохраняли свои значения!) работали должным образом.   -  person Peter Cordes    schedule 31.08.2016
comment
Вы уверены, что переменная хранится в регистре? Нельзя ли его сохранить в стеке? В этом случае у вас может быть ошибка, которая повреждает стек в someNestedFunction. Попробуйте комментировать его фрагменты, пока не сузите проблему до виновника (это, конечно, предполагает, что комментирование вызова someNestedFunction в первую очередь решает проблему)   -  person    schedule 31.08.2016
comment
замена const на volatile меняет ситуацию?   -  person Serge    schedule 31.08.2016
comment
Согласно ABI, s16-s31 callee-saved, поэтому одна из промежуточных функций (или, возможно, обработчик прерывания) либо повреждает стек, либо трогает регистры FP за спиной компилятора. Вы хотите отлаживать и исправлять это, а не ходить вокруг да около в этой функции. Кроме того, автоматическое стекирование регистров, сохраненных вызывающим абонентом, при вводе исключения, о котором вы упоминали, на самом деле здесь ни к чему не имеет никакого отношения.   -  person Notlikethat    schedule 31.08.2016
comment
@Serge Даже если это поможет, это не устранит причину проблемы.   -  person mactro    schedule 31.08.2016
comment
@Notlikethat Что вы подразумеваете под прикосновением к регистрам FP за спиной компилятора?   -  person mactro    schedule 31.08.2016
comment
@mactro да, но это может помочь определить источник проблемы   -  person Serge    schedule 31.08.2016
comment
Если компилятор генерирует код, который касается регистра, сохраненного вызываемым пользователем, он также сгенерирует необходимый пролог/эпилог для сохранения и восстановления предыдущего значения. Если какой-либо неправильный встроенный (или внешний) ассемблер или другой обман приводят к тому, что компилятор не выдает этот код пролога/эпилога (потому что он не знает о касании регистров) или поток управления каким-то образом обходит его, тогда все ставки выключены.   -  person Notlikethat    schedule 31.08.2016


Ответы (1)


Хорошая детективная работа, чтобы обнаружить, что регистр сохраняется как D9.

О каком неинициализированном мусоре в snprintf вы говорите? Вы передали поддельный указатель? Если это так, то snprintf, вероятно, перезаписал место в стеке, где ваша переменная сохранялась/восстанавливалась в/из, потому что полученный указатель buf сказал ему сделать это.

Повреждение данных в стеке обычно приводит к неверным адресам возврата (вызывающим segfaults) или неверным данным в переменных, которые должны пережить вызовы функций.


обновление: если это был просто неинициализированный поплавок, это ничего не объясняет.

Предложение: установите точки останова в самом удаленном месте, где D9 сохраняется и восстанавливается. Установите точку наблюдения на адрес стека, где она сохранена, чтобы вы могли определить, когда память перезаписывается, в то время как она все еще содержит сохраненное значение вашей переменной.

person Peter Cordes    schedule 02.09.2016
comment
Вызов выглядит так: char msg[10]; snprintf(msg, 10, "%4.2f", value/100.0f);, где значение было числом с плавающей запятой, которое не было инициализировано. - person mactro; 02.09.2016
comment
@mactro: Хм, чтение неинициализированного поплавка этого не объясняет. :/ snprintf по-прежнему должен просто печатать NaN или несколько десятичных цифр в msg и всегда завершает строку нулем. (В отличие от strncpy). - person Peter Cordes; 03.09.2016