Поведение LDR для переменной uint8_t в ARM? - PullRequest
0 голосов
/ 26 января 2020

Я не могу найти прямой ответ на этот вопрос нигде. Регистры для ARM являются 32-разрядными, я знаю, что LDRB загружает значение размера байта в регистр и обнуляет оставшиеся 3 байта, даже если вы передадите ему значение больше байта, оно просто примет первое значение байта.

Моя программа объединяет C с ARM Assembly. У меня есть переменная extern в C, которая загружается в регистр напрямую.

Однако, если я вызову только LDR для этой байтовой переменной, есть ли гарантия, что он загружает байт и ничего больше, или он загрузит случайные вещи в оставшемся 3-байтовом пространстве из соседних вещей в памяти, чтобы заполнить весь 32-битный регистр?

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

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

Это компилятор? Может ли он обнаружить это и автоматически переключиться на LDRB? Или мне просто повезло, что окружающая память переменной была просто нулевой из-за некоторой оптимизации?

В качестве примечания, компилятор - ARM G CC 9.2.1

Ответы [ 2 ]

2 голосов
/ 26 января 2020

потому что я сделал LDR R0, =var

Вы загружаете значение или адрес переменной?

Обычно инструкция LDR R0, =var записывает адрес переменной var в регистр R0, а не значение.

И адрес переменной всегда является 32-битным значением на 32-разрядный процессор ARM - независимо от типа данных.

Однако, если я вызову только LDR для этой байтовой переменной, ...

Если вы загрузите значение переменной (например, с использованием LDR R1, [R0]) могут происходить две вещи:

  • Старшие 24 бита регистра могут содержать случайное значение в зависимости от следующих байтов ваша переменная в памяти. Если вам повезет, байты всегда равны нулю.
  • В зависимости от точного типа процессора могут возникнуть проблемы из-за выравнивания (например, исключение выравнивания или даже совершенно неопределенное поведение)
0 голосов
/ 26 января 2020

LDR ничего не знает о том, как вы объявили переменную или что должно быть в 4 байтах, которые она загружает. Вот почему ISA, такие как ARM, в первую очередь имеют байтовые нагрузки, такие как LDRB (и его эквивалент с расширением знака).

И нет, компиляторы не тратят 3 байта (нулей) после каждого uint8_t просто так Вы можете использовать словесные нагрузки на это, это было бы глупо. то есть sizeof(uint8_t) = 1 = беззнаковый символ, CHAR_BIT = 8 и alignof(uint8_t) = 1

LDR загружает int32_t или uint32_t целое слово.

Но как Мартин указывает, что LDR r0, =var помещает адрес var в регистр.
Затем вы используете ldrb r1, [r0]

Забавный факт: ранние процессоры ARM (ARMv4 и более ранние) с загрузка невыровненного слова будет использовать младшие 2 бита адреса в качестве счетчика поворота (после загрузки из выровненного слова). https://medium.com/@iLevex /-любопытны случай-оф-выровненным-доступ-на-руки-5dd0ebe24965

...