Search code examples
optimizationconstantsada

How well can Ada optimize static constant arrays?


Let's say I declare an array of constant values for use as a lookup table:

function Square(num : Integer) return Integer is
use Ada.Text_IO;
type LookupItemsRange is range 1..10;
type LookupTable is array(LookupItemsRange) of integer;
A : constant LookupTable := (2312,2,3,4,5,6,7,8,9, 10);
begin
    Sqr := A(1);
    Sqr := 2 * A(2);
    return Sqr;
end Square;

The LookupTable shouldn't use any space. The compiler should be able to substitute array access with the actual inlined values. At the highest optimization setting, here's what Ada yields:

_ada_square:
        li      a0,4                #,
        ret     

I am planning on substituting a large amount of redundant MCU code with a cleaner lookup table implementation. Since the microcontroller's resources are constrained, I would like to avoid needless allocation and indexing.

Under what circumstances will Ada allocate the array or am I completely safe?


Solution

  • So for the Ada language specification, you won't get a guarantee for array objects. Ada is starting to catch up to constexpr in the newest upcoming release (Ada2022), but will only be providing it's analogous property (Static in Ada) to expression functions (think of them analogous to constexpr functions but with slightly different limitations).

    So if you don't mind relying on your compiler vendor, then the array will most likely work fine (but is not guaranteed). If you want a more language supported guarantee on the optimization, then you will need to mimic the array using expression functions with the Static aspect set (in a recent compiler). For a static input, a static expression function will give a static result (again Static in Ada, not static in c++, more like constexpr).

    function Square return Integer is
    
        type Lookup_Items_Range is range 1..10;
    
        function A(Index : Lookup_Items_Range) return Integer is 
            (case Index is
                when 1 => 2312,
                when 2 => 2,
                when 3 => 3,
                when 4 => 4,
                when 5 => 5,
                when 6 => 6,
                when 7 => 7,
                when 8 => 8,
                when 9 => 9,
                when 10 => 10
             ) 
        with Static;
    
        Sqr : Integer;
    
    begin
    
        Sqr := A(1);
        Sqr := 2 * A(2);
        return Sqr;
        
    end Square;
    

    It's slightly more verbose than using an array, but has the language backing, which is really important in certain embedded programming areas. Note, most recent compilers will need a switch to enable this feature since it is so new, but they generally tell you that switch in the error message. I tested on godbolt with GNAT 12.1 and it compiled with the correct switch.