AdaCore / Ada_Drivers_Library

Ada source code and complete sample GNAT projects for selected bare-board platforms supported by GNAT.
BSD 3-Clause "New" or "Revised" License
248 stars 144 forks source link

Add Cortex_M.Hints, which provides access to hint instructions #387

Closed JeremyGrosser closed 3 years ago

JeremyGrosser commented 3 years ago

Send_Event, Wait_For_Event, Wait_For_Interrupt, and Yield are defined as "hint instructions" in the ARMv6-M Architecture Reference Manual. This package just conveniently wraps them up as procedures, very similar to the Memory_Barriers package in the same directory.

When testing this package, I discovered that GNAT's Inline is really just a suggestion. Unless -fomit-frame-pointer is added to the compiler flags, five additional instructions are added to each of these calls to save and restore the link register. Here is the disassembled output of Wait_For_Interrupt annotated with source.

   procedure Wait_For_Interrupt is
     908:       b580            push    {r7, lr}
     90a:       af00            add     r7, sp, #0
      pragma Suppress (All_Checks);
   begin
      Asm ("wfi", Volatile => True);
     90c:       bf30            wfi
   end Wait_For_Interrupt;
     90e:       46bd            mov     sp, r7
     910:       bd80            pop     {r7, pc}
     912:       46c0            nop                     ; (mov r8, r8)

Even with -fomit-frame-pointer, this function still requires a bl branch with link to the wfi instruction, followed by a bx lr afterward.

Is there any aspect or pragma that can be used to cause GNAT to truly inline these instructions without branching or manipulating the stack?

JeremyGrosser commented 3 years ago

Upon further investigation, enabling Link Time Optimization by adding -flto to both the compiler and linker flags does indeed inline this without branching.

Fabien-Chouteau commented 3 years ago

I think the compiler should inline as soon as you enable some -O2 or -O3 optimization at least. There is also -gnatn to enable inlining.

And you can give a hints to the compiler with pragma Inline (Yield); at the end of the specification for instance.

JeremyGrosser commented 3 years ago

I think the compiler should inline as soon as you enable some -O2 or -O3 optimization at least. There is also -gnatn to enable inlining.

And you can give a hints to the compiler with pragma Inline (Yield); at the end of the specification for instance.

I already have with Inline on all of the procedures in the specification, the Ada 2012 reference says this is preferred over the pragma now.

It looks like adding -O3 removes the link register manipulation and -gnatn gets rid of the branching. I would've expected that with Inline or -O3 would've implicitly enabled inlining, but apparently not!

The GNAT user guide makes this set of conditions clear here https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gnat_ugn/Inlining-of-Subprograms.html#g_t10f

Fabien-Chouteau commented 3 years ago

@JeremyGrosser thanks for the contribution. The builds are failing because of another issue in the scripts (see #388). I'll ask you to rebase once this is fixed.

Fabien-Chouteau commented 3 years ago

@JeremyGrosser if you can rebase on the master branch that would be great :)

Fabien-Chouteau commented 3 years ago

Thank you for your contribution @JeremyGrosser, and sorry for the issue with the build script.