comp.lang.ada
 help / color / mirror / Atom feed
* Odd AVR-Ada code generation issue with constant record type
@ 2019-07-05  8:57 Per Jakobsen
  2019-07-05  9:24 ` Mark Lorenzen
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Per Jakobsen @ 2019-07-05  8:57 UTC (permalink / raw)


I'm trying to demonstrate some of Ada's strengths with regard to low-level programming, and ran into the following issue:

Having a byte-record consisting of bit-fields of different types (typical microcontroller register definitions). Compiling the below Ada code using avr-ada 1.2.2 (avr-gnat 4.9.3) gives some odd assembly code.

Summary:
All assignments is really constants that ought to be calculated in full at compile time.

1) Clearing all bitfields in the byte as one assignment, it loads, clears and stores for each field even though the result is constant. *Very* inefficient and even dangerous if the bitfields are assumed to be set atomically!!!

2) Setting one field (any), a calculated byte constant is stored directly.

3) Setting more than one field, a calculated byte constant is initialized in SRAM and used during execution. Inefficient.

4) Clearing a non-composite byte is done directly.

5) Setting a non-composite byte is done directly.


Is there an option or pragma I need to set, or is this something more fundamental with the compiler?

Code:
------------------------------------
### main.adb, compiled with Makefile and build.gpr from uart_echo in avr-ada package.


with AVR;
with System;
with Interfaces;

procedure Main is
   use AVR;
   use Interfaces;

   type Enum_Type is (B00, B01, B10, B11);
   for Enum_Type use (B00 => 0,
                      B01 => 1,
                      B10 => 2,
                      B11 => 3);

   type List_Type is array (0 .. 4) of Boolean with Pack;

   type Reg_Type is record
      Enum : Enum_Type;
      Bool : Boolean;
      List : List_Type;
   end record;
   for Reg_Type use record
      Enum at 0 range 6 .. 7;
      Bool at 0 range 5 .. 5;
      List at 0 range 0 .. 4;
   end record;
   for Reg_Type'Size use 8;

   --  Reg_A  --
   Reg_A : Reg_Type;
   for Reg_A'Address use System.Address (16#7a#);
   pragma Volatile (Reg_A);

   --  Reg_B  --
   Reg_B : Reg_Type;
   for Reg_B'Address use System.Address (16#7b#);
   pragma Volatile (Reg_B);

   --  Reg_C  --
   Reg_C : Reg_Type;
   for Reg_C'Address use System.Address (16#7c#);
   pragma Volatile (Reg_C);

   --  Reg_D  --
   Reg_D : Unsigned_8;
   for Reg_D'Address use System.Address (16#7d#);
   pragma Volatile (Reg_D);

   --  Reg_E  --
   Reg_E : Unsigned_8;
   for Reg_E'Address use System.Address (16#7e#);
   pragma Volatile (Reg_E);

begin
   --  Set composite with resulting zero-byte.
   Reg_A := (Enum => B00,
             Bool => False,
             List => (others => False));
   --  Generated assembly:
   --     ldi     r30, 0x7A       ; Set address of register
   --     ldi     r31, 0x00
   --     ld      r24, Z          ; Load
   --     andi    r24, 0xE0       ; Clear List.
   --     st      Z, r24          ; Store
   --     ld      r18, Z          ; Load
   --     andi    r18, 0xDF       ; Clear Bool.
   --     st      Z, r18          ; Store
   --     ld      r19, Z          ; Load
   --     andi    r19, 0x3F       ; Clear Enum.
   --     st      Z, r19          ; Store
   --  Result : 22 bytes code, 0 bytes SRAM, 17 cycles
   --  Optimal:  4 bytes code, 0 bytes SRAM,  2 cycles

   --  Set composite with resulting non-zero-byte (any one field is set, others zero).
   Reg_B := (Enum => B00,
             Bool => True,
             List => (others => False));
   --  Generated assembly:
   --     ldi     r20, 0x20       ; Set register bits
   --     sts     0x007B, r20     ; Store
   --  Result: 6 bytes code, 0 bytes SRAM, 3 cycles
   --  Is optimal!

   --  Set composite with resulting non-zero-byte (multiple fields is set).
   Reg_C := (Enum => B11,
             Bool => True,
             List => (others => True));
   --  Generated assembly:
   --     lds   r21, 0x0100       ; Load pre-initialized SRAM-stored constant.
   --     sts   0x007C, r21       ; Store
   --  Result : 8 bytes code, 1 bytes SRAM, 4 cycles, requires initialization.
   --  Optimal: 6 bytes code, 0 bytes SRAM, 3 cycles, no initialization.

   --  Clear Unsigned_8 directly.
   Reg_D := 16#00#;
   --  Generated assembly:
   --     sts   0x007D, r1        ; Store with "reserved" zero-register.
   --  Result: 4 bytes code, 0 bytes SRAM, 2 cycles
   --  Is optimal!

   --  Setting Unsigned_8 directly.
   Reg_E := 16#FF#;
   --  Generated assembly:
   --     ldi   r22, 0xFF         ; Load constant.
   --     sts   0x007E, r22       ; Store
   --  Result: 6 bytes code, 0 bytes SRAM, 3 cycles
   --  Is optimal!

end Main;


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Odd AVR-Ada code generation issue with constant record type
  2019-07-05  8:57 Odd AVR-Ada code generation issue with constant record type Per Jakobsen
@ 2019-07-05  9:24 ` Mark Lorenzen
  2019-07-05 12:19 ` Per Jakobsen
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Mark Lorenzen @ 2019-07-05  9:24 UTC (permalink / raw)


On Friday, July 5, 2019 at 10:57:42 AM UTC+2, Per Jakobsen wrote:
> I'm trying to demonstrate some of Ada's strengths with regard to low-level programming, and ran into the following issue:
> 
> Having a byte-record consisting of bit-fields of different types (typical microcontroller register definitions). Compiling the below Ada code using avr-ada 1.2.2 (avr-gnat 4.9.3) gives some odd assembly code.
> 
> Summary:
> All assignments is really constants that ought to be calculated in full at compile time.
> 
> 1) Clearing all bitfields in the byte as one assignment, it loads, clears and stores for each field even though the result is constant. *Very* inefficient and even dangerous if the bitfields are assumed to be set atomically!!!
> 
> 2) Setting one field (any), a calculated byte constant is stored directly.
> 
> 3) Setting more than one field, a calculated byte constant is initialized in SRAM and used during execution. Inefficient.
> 
> 4) Clearing a non-composite byte is done directly.
> 
> 5) Setting a non-composite byte is done directly.
> 
> 
> Is there an option or pragma I need to set, or is this something more fundamental with the compiler?
> 

First of all you are using an old compiler... Maybe upgrading the compiler (if possible) would help.

I would (in the following order):
1) declare the registers as Atomic (or Atomic and Volatile but that doesn't matter as Atomic implies Volatile).
2) define constants for the values you are assigning.

Regards,
Mark L


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Odd AVR-Ada code generation issue with constant record type
  2019-07-05  8:57 Odd AVR-Ada code generation issue with constant record type Per Jakobsen
  2019-07-05  9:24 ` Mark Lorenzen
@ 2019-07-05 12:19 ` Per Jakobsen
  2019-07-08 14:49   ` Stéphane Rivière
  2019-07-09 12:12 ` Per Jakobsen
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 8+ messages in thread
From: Per Jakobsen @ 2019-07-05 12:19 UTC (permalink / raw)



> First of all you are using an old compiler... Maybe upgrading the compiler (if possible) would help.

It might, although I *am* using the newest avr-ada version I can find. If there is a newer I will certainly try!?


> I would (in the following order):
> 1) declare the registers as Atomic (or Atomic and Volatile but that doesn't matter as Atomic implies Volatile).

Declaring the registers Atomic certainly helped a lot - No more run-time "calculations", although all constants are put into SRAM (like Reg_C above), instead of pure "LDI" instructions (Reg_B).


> 2) define constants for the values you are assigning.

Does not help - Exactly the same behavior.


Thanks for the Atomic hint!

Anyone knows if there is a newer version of avr-ada out there? Or have instructions on how to create a new?


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Odd AVR-Ada code generation issue with constant record type
  2019-07-05 12:19 ` Per Jakobsen
@ 2019-07-08 14:49   ` Stéphane Rivière
  0 siblings, 0 replies; 8+ messages in thread
From: Stéphane Rivière @ 2019-07-08 14:49 UTC (permalink / raw)


Hi Per,

> Anyone knows if there is a newer version of avr-ada out there? Or have instructions on how to create a new?

Not really helpfull but... at 2017118...

https://stef.genesix.org/pub/embedded_AVR-Ada_-_Setup_POSS2017.pdf

See page 59 - Releases chapter...

The john Leimon fork is here...

https://github.com/evilspacepirate/avr-ada

If you find more accurate info... I would like to hear about it :)

All the best from Oleron Island - France

-- 
Be Seeing You
Number Six


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Odd AVR-Ada code generation issue with constant record type
  2019-07-05  8:57 Odd AVR-Ada code generation issue with constant record type Per Jakobsen
  2019-07-05  9:24 ` Mark Lorenzen
  2019-07-05 12:19 ` Per Jakobsen
@ 2019-07-09 12:12 ` Per Jakobsen
  2019-07-09 15:30 ` Simon Wright
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Per Jakobsen @ 2019-07-09 12:12 UTC (permalink / raw)


Hi Stéphane

Thanks for the link to "embedded AVR-Ada - Setup", I'm going to read it through more thoroughly later this week :-)

All the projects appear a bit dated all using 4.9.
I've tried AVR-Ada and the AVR-Ada-JLF projects, both failing when building on my setup.

Greetings from DK

~Per


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Odd AVR-Ada code generation issue with constant record type
  2019-07-05  8:57 Odd AVR-Ada code generation issue with constant record type Per Jakobsen
                   ` (2 preceding siblings ...)
  2019-07-09 12:12 ` Per Jakobsen
@ 2019-07-09 15:30 ` Simon Wright
  2019-07-09 18:10 ` Per Jakobsen
  2019-07-09 18:46 ` Per Jakobsen
  5 siblings, 0 replies; 8+ messages in thread
From: Simon Wright @ 2019-07-09 15:30 UTC (permalink / raw)


I don't have access to an up-to-date AVR compiler, so I had a go with
GCC 9.1 compiling for cortex-m4. There's a big improvement using
Volatile_Full_Access rather than just Volatile.

This code
========================================================================
with System;
with Interfaces;

procedure Main is
   use Interfaces;

   type Enum_Type is (B00, B01, B10, B11);
   for Enum_Type use (B00 => 0,
                      B01 => 1,
                      B10 => 2,
                      B11 => 3);

   type List_Type is array (0 .. 4) of Boolean with Pack;

   type Reg_Type is record
      Enum : Enum_Type;
      Bool : Boolean;
      List : List_Type;
   end record;
   for Reg_Type use record
      Enum at 0 range 6 .. 7;
      Bool at 0 range 5 .. 5;
      List at 0 range 0 .. 4;
   end record;
   for Reg_Type'Size use 8;

   --  Reg_A  --
   Reg_A : Reg_Type
     with
       Volatile_Full_Access,
       Address => System'To_Address (16#7a#);

   --  Reg_B  --
   Reg_B : Reg_Type
     with
       Volatile_Full_Access,
       Address => System'To_Address (16#7b#);

   --  Reg_C  --
   Reg_C : Reg_Type
     with
       Volatile_Full_Access,
       Address => System'To_Address (16#7c#);

   --  Reg_D  --
   Reg_D : Unsigned_8
     with
       Volatile_Full_Access,
       Address => System'To_Address (16#7d#);

   --  Reg_E  --
   Reg_E : Unsigned_8
     with
       Volatile_Full_Access,
       Address => System'To_Address (16#7e#);

begin
   --  Set composite with resulting zero-byte.
   Reg_A := (Enum => B00,
             Bool => False,
             List => (others => False));

   --  Set composite with resulting non-zero-byte (any one field is set, others zero).
   Reg_B := (Enum => B00,
             Bool => True,
             List => (others => False));

   --  Set composite with resulting non-zero-byte (multiple fields is set).
   Reg_C := (Enum => B11,
             Bool => True,
             List => (others => True));

   --  Clear Unsigned_8 directly.
   Reg_D := 16#00#;

   --  Setting Unsigned_8 directly.
   Reg_E := 16#FF#;

end Main;
========================================================================

compiled with -O2 yields

========================================================================
Disassembly of section .text.startup._ada_main:

00000000 <_ada_main>:
   0:	4b0a      	ldr	r3, [pc, #40]	; (2c <_ada_main+0x2c>)
   2:	4a0b      	ldr	r2, [pc, #44]	; (30 <_ada_main+0x30>)
   4:	480b      	ldr	r0, [pc, #44]	; (34 <_ada_main+0x34>)
   6:	217a      	movs	r1, #122	; 0x7a
   8:	b410      	push	{r4}
   a:	781c      	ldrb	r4, [r3, #0]
   c:	700c      	strb	r4, [r1, #0]
   e:	237b      	movs	r3, #123	; 0x7b
  10:	7812      	ldrb	r2, [r2, #0]
  12:	701a      	strb	r2, [r3, #0]
  14:	227c      	movs	r2, #124	; 0x7c
  16:	7800      	ldrb	r0, [r0, #0]
  18:	7010      	strb	r0, [r2, #0]
  1a:	217d      	movs	r1, #125	; 0x7d
  1c:	237e      	movs	r3, #126	; 0x7e
  1e:	2000      	movs	r0, #0
  20:	22ff      	movs	r2, #255	; 0xff
  22:	7008      	strb	r0, [r1, #0]
  24:	701a      	strb	r2, [r3, #0]
  26:	f85d 4b04 	ldr.w	r4, [sp], #4
  2a:	4770      	bx	lr
========================================================================


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Odd AVR-Ada code generation issue with constant record type
  2019-07-05  8:57 Odd AVR-Ada code generation issue with constant record type Per Jakobsen
                   ` (3 preceding siblings ...)
  2019-07-09 15:30 ` Simon Wright
@ 2019-07-09 18:10 ` Per Jakobsen
  2019-07-09 18:46 ` Per Jakobsen
  5 siblings, 0 replies; 8+ messages in thread
From: Per Jakobsen @ 2019-07-09 18:10 UTC (permalink / raw)


Right, so even GCC 9.1 chooses to load a constant from memory instead of loading as immediate, 2-3 cycles instead of 1 as well as using additional memory space. I wonder why GCC does not optimize this... It seems like a simple optimization, but then again, I'm no compiler developer so easy for me to say...

I just tried with arm-linux-gnueabihf-gnatmake and x86_64-linux-gnu-gnatmake both version 8.3, and both use the slower load from memory instead of load immediate.

I wonder if it is an Ada-specific thing, or a general GCC back-end deficiency?...

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Odd AVR-Ada code generation issue with constant record type
  2019-07-05  8:57 Odd AVR-Ada code generation issue with constant record type Per Jakobsen
                   ` (4 preceding siblings ...)
  2019-07-09 18:10 ` Per Jakobsen
@ 2019-07-09 18:46 ` Per Jakobsen
  5 siblings, 0 replies; 8+ messages in thread
From: Per Jakobsen @ 2019-07-09 18:46 UTC (permalink / raw)


Just tried with C bitfields:

================================
#include <stdint.h>

struct Reg_T {
        unsigned int a : 3;
        unsigned int b : 1;
        unsigned int c : 4;
} __attribute__((packed));


volatile struct Reg_T Reg_A;
volatile struct Reg_T Reg_B;
volatile struct Reg_T Reg_C;
volatile uint8_t A;
volatile uint8_t B;

void main (void)
{
	Reg_A = (struct Reg_T){ .a = 0, .b = 0, .c = 0 };
	Reg_B = (struct Reg_T){ .a = 0, .b = 1, .c = 0 };
	Reg_C = (struct Reg_T){ .a = 7, .b = 1, .c = 15 }

        A = 0;
        B = 255;
}
================================

Same type of result...
GCC back-end then?

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2019-07-09 18:46 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-05  8:57 Odd AVR-Ada code generation issue with constant record type Per Jakobsen
2019-07-05  9:24 ` Mark Lorenzen
2019-07-05 12:19 ` Per Jakobsen
2019-07-08 14:49   ` Stéphane Rivière
2019-07-09 12:12 ` Per Jakobsen
2019-07-09 15:30 ` Simon Wright
2019-07-09 18:10 ` Per Jakobsen
2019-07-09 18:46 ` Per Jakobsen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox