Fix creation of Huffman decoding tree for big-endian platforms

Attempting to run demos/hello_world on ppc-linux yields a constraint error
at startup:

> raised CONSTRAINT_ERROR : aws-http2-hpack-huffman.adb:358 discriminant check failed

This occurs during the elaboration of AWS.HTTP2.HPACK.Huffman, in
Create_Tree.Insert_Item: we reach a point where we expect a non-leaf node
and attempt to access its children via .LR, but we actually get a leaf node.

This can be traced back to ppc-linux being a big-endian platform, and the
iteration over C.Bits being sensitive to endianness.  Since we alias an
Unsigned_32 as a Word_Bits array, the bit order will depend on the platform
representation.  Consider this example:

  -- alias.adb
  with Ada.Text_IO;
  with Ada.Integer_Text_IO;
  with Interfaces;
  use Interfaces;

  procedure Alias is
     subtype Bit is Integer range 0 .. 1;
     type Word_Bits is array (0 .. 31) of Bit with Pack;

     package U32_IO is new Ada.Text_IO.Modular_IO (Interfaces.Unsigned_32);

     Foo : Interfaces.Unsigned_32 := 16#04030201#;
     Bar : Word_Bits with Size => 32, Address => Foo'Address;
  begin
     U32_IO.Put (Foo, Base => 16);
     Ada.Text_IO.New_Line;
     U32_IO.Put (Foo, Base => 2);
     Ada.Text_IO.New_Line;

     for B in Bar'Range loop
        U32_IO.Put (Interfaces.Shift_Right (Foo, B) and 1, Width => 1);
        if B mod 8 = 7 then
           Ada.Text_IO.Put(' ');
        end if;
     end loop;
     Ada.Text_IO.New_Line;

     for B in Bar'Range loop
        Ada.Integer_Text_IO.Put (Bar (B), Width => 1);
        if B mod 8 = 7 then
           Ada.Text_IO.Put(' ');
        end if;
     end loop;
     Ada.Text_IO.New_Line;
  end Alias;

On x86_64-linux, this prints:

  16#4030201#
  2#100000000110000001000000001#
  10000000 01000000 11000000 00100000
  10000000 01000000 11000000 00100000

Whereas on ppc-linux, we get instead:

  16#4030201#
  2#100000000110000001000000001#
  10000000 01000000 11000000 00100000
  00000100 00000011 00000010 00000001

From this, we can observe:

* that Modular_IO's representation is always big-endian, with leading zeroes
  suppressed:
  * in base 16, we always get 16#(0)4#, then 16#03#, etc,
  * in base 2, we always get 2#(00000)100#, then 2#00000011#, etc;

* that the results of shifting are platform-independent: a shift operates on
  the numerical value of the integer; whatever endianness a CPU uses, it
  will always implement a right-shift so that X>>n = X/(2^n);

* that aliasing an Unsigned_32 as an array of bits exposes the platform
  endianness: a different endianness leads to a different bit order.

While it is not obvious why changing the iteration order over C.Bits
invalidates Create_Tree.Insert_Item's graph traversal, to the point of
mistaking a leaf node for a branch node, fixing this order does let this
elaboration code run to completion on ppc-linux, and introduces no
regression when running the testsuite on x86_64-linux.

This bug could also have been fixed by sprinkling Scalar_Storage_Order and
Bit_Order representation clauses over Word_Bits and Code; going with shifts
felt more straightforward.

TN: W214-015
This commit is contained in:
Kévin Le Gouguec
2023-02-16 09:08:13 +01:00
parent cc09ae1dd1
commit 7eca5a10cb

View File

@@ -329,7 +329,6 @@ package body AWS.HTTP2.HPACK.Huffman is
-----------------
procedure Create_Tree is
type Word_Bits is array (0 .. 31) of Bit with Pack;
procedure Insert_Item (K : Unsigned_16);
@@ -339,14 +338,11 @@ package body AWS.HTTP2.HPACK.Huffman is
procedure Insert_Item (K : Unsigned_16) is
C : constant Code := Table (K);
H : Unsigned_32 := C.Bits;
Bits : Word_Bits with Size => 32, Address => H'Address;
Iter : access Node_Access := Root'Access;
begin
for K in reverse 0 .. C.N_Bit - 1 loop
declare
B : constant Bit := Bits (K);
B : constant Bit := Bit (Shift_Right (C.Bits, K) and 1);
begin
if Iter.all = null then
Iter.all := new Node'(False, LR => (null, null));