Skip to content

Error: registers may not be the same — `strexh r0,r0,[r1]‘

by admin on November 4th, 2016

core_cm3.c

http://we.easyelectronics.ru/PahanMenski/gcc-46-i-cmsis-ispravlyaem-oshibku-kompilyacii.html

В процессе изучения STM32 решил я обновить компилятор. Сказано — сделано. Свеженький Sourcery G++ Lite с GCC 4.6.1 на борту был скачен и установлен. Вот только проекты, использующие CMSIS дружно перестали компилироваться выдавая ошибки:

Error: registers may not be the same -- `strexh r0,r0,[r1]'
Error: registers may not be the same -- `strexb r0,r0,[r1]'


Гугление быстро выявило наличие данного бага в баг-трекере GCC. Впрочем, как оказалось, это вовсе не баг, а фича текущей версии. Все дело в том, что использование одного и того же регистра в качестве первого и второго операндов данных команд приводит, согласно спецификации ARMv7m, к неопределенному поведению. Кстати, в документации от STM32 об этом не говорится, и работает, насколько я понимаю, правильно. Тем не менее, спеку надо чтить, поэтому поищем, как исправить это досадное недоразумение.
На просторах интернета встречаются два решения:

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
   uint32_t result=0;
   __ASM volatile ("strexh r2, %2, [%1] \n" \
       "    mov %0, r2" : "=r" (result) : "r" (addr), "r" (value) : "r2" );
   return(result);
}

и

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
   register uint32_t result asm("r2");
   __ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   return(result);
}

Очевидным минусом является явное использование регистра r2. Все же задачу выбора свободного регистра лучше было бы поручить компилятору. Попробуем сделать это!
Итак, мы хотим, чтобы в качестве выходного параметра использовался регистр, отличный от входных. В GCC мы можем использовать знак амперсанда, чтобы указать, что регистр должен использоваться исключительно как выходной. Таким образом, просто добавим знак амперсанда после символа =

uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
   uint32_t result=0;
   __ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value));
   return(result);
}

Профит! Теперь компилятор сам назначит свободный регистр. Получаемый после компиляции код одинаков во всех случаях, однако последнее решение наиболее красиво и, к тому же, требует минимальных изменений.

https://gist.github.com/timbrom/1942280

From 7341a316c38a61cc745ce04e618916677007883b Mon Sep 17 00:00:00 2001
From: Timothy Brom <thb@timbrom.com>
Date: Wed, 29 Feb 2012 11:29:55 -0500
Subject: [PATCH] Fixed error about strexh and strexb using the same register
---
example/libs_stm/inc/core_support/core_cm3.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/example/libs_stm/inc/core_support/core_cm3.c b/example/libs_stm/inc/core_support/core_cm3.c
index 56fddc5..0e8c3c4 100644
--- a/example/libs_stm/inc/core_support/core_cm3.c
+++ b/example/libs_stm/inc/core_support/core_cm3.c
@@ -733,7 +733,7 @@ uint32_t __STREXB(uint8_t value, uint8_t *addr)
{
uint32_t result=0;
- __ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
+ __ASM volatile ("strexb %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );
return(result);
}
@@ -750,7 +750,7 @@ uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
uint32_t result=0;
- __ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
+ __ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) );
return(result);
}
--
1.7.5.4

From → Coocox, STM32, STM32F100

No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS