Incorrect register allocation in inline assembly code

Bug #1720993 reported by Oleg Skydan on 2017-10-03
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
GNU ARM Embedded Toolchain
Undecided
Unassigned

Bug Description

I found a problem with register allocation for the following code:

   uint32_t __basepri__;
   uint32_t x = 0xE0;
   asm volatile("mrs %0,BASEPRI \n\t"
                "msr BASEPRI_MAX,%1 \n\t"
                     : "=r" (__basepri__) : "r" (x));

Here is the listing:

   6:2.c **** uint32_t __basepri__;
   7:2.c **** uint32_t x = 0xE0;
   8:2.c **** asm volatile("mrs %0,BASEPRI \n\t"
  31 .loc 1 8 0
  32 0000 E023 movs r3, #224
  33 .syntax unified
  34 @ 8 "2.c" 1
  35 0002 EFF31183 mrs r3,BASEPRI
  36 0006 83F31288 msr BASEPRI_MAX,r3
  37

As you can see R3 register is allocated for both input and output data, so the first assembler instruction overwrites the input data for the second one and code works incorrectly.

I use GNU Arm Embedded Toolchain Version 6-2017-q2-update (Released: June 28, 2017). The problem also present in GNU Arm Embedded Toolchain Version 6-2017-q1.

Changed in gcc-arm-embedded:
status: New → Invalid

You need to use the & constraint modifier on your output operand. Alternatively you could reorder the two instructions and keep the constraints as is, which would be the most efficient.

From the official GCC documentation [1]:

"Use the ‘&’ constraint modifier (see Modifiers) on all output operands that must not overlap an input. Otherwise, GCC may allocate the output operand in the same register as an unrelated input operand, on the assumption that the assembler code consumes its inputs before producing outputs. This assumption may be false if the assembler code actually consists of more than one instruction."

[1] https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#OutputOperands

Best regards,

Thomas

BTW, the reason is that GCC does not understand what is inside the inline assembly statement. This means it does not realize there is two instructions and one happens before the other. Anyway for some architecture one could imagine that two consecutive instructions would happen together and have no impact on each other.

Therefore GCC will assume that the input and output will not overlap, ie. the input will be read before the output happens just like what happens for a single instruction(ie add r3, r3, r3 where r3 is read before it is written). This is why we need to tell GCC that there is an overlap (output happens before input is consumed).

Best regards.

Oleg Skydan (oskidan) wrote :

Thomas, Thanks for clarification.

>Alternatively you could reorder the two instructions and keep the constraints as is, which would be the most efficient.

Reodering is not an option here, cause BASEPRI and BASEPRI_MAX is actually the same register.

Best regards,
Oleg

To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers