[Constraint_Error,当使用不能被8整除的模块化类型时

问题描述 投票:1回答:3

我遇到了一个问题,即在Ada中使用无法被系统Storage_Unit(在运行时的system.ads中定义)可整除的模块化类型,将在运行时在运行时产生一个Constraint_Error。我最初在使用最少运行时间的裸机系统上工作时遇到此问题,同时尝试通过将12bit数组覆盖在内存中的缓冲区上来从缓冲区读取12bit值。有人知道为什么会这样吗?

以下最小示例说明了我遇到的问题。我使用AdaCore的GNAT 2019(包含的zfp运行时进行编译)对此进行了测试。使用标准运行时不会重现该问题。

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   ----------------------------------------------------------------------------
   --  Modular type with standard size divisible by 8.
   ----------------------------------------------------------------------------
   type Type_One is mod 2 ** 16;
   type Buffer_Type_One is array (1 .. 128) of Type_One;

   ----------------------------------------------------------------------------
   --  Modular type with non-base 8 size.
   ----------------------------------------------------------------------------
   type Type_Two is mod 2 ** 12;
   type Buffer_Type_Two is array (1 .. 128) of Type_Two;

   type Buffer is array (1 .. 256) of Character;

   ----------------------------------------------------------------------------
   --  Example buffer.
   ----------------------------------------------------------------------------
   Test_Buffer : Buffer := (others => ' ');
begin
   ----------------------------------------------------------------------------
   --  Will not raise an exception.
   ----------------------------------------------------------------------------
   Test_One :
      declare
         Buffer_One : Buffer_Type_One
         with Import,
         Convention => Ada,
         Address    => Test_Buffer'Address;
      begin
         Put_Line ("Testing type one");
         for I in Natural range 1 .. 16 loop
            Put_Line ("Test: " & Buffer_One (I)'Image);
         end loop;
      end Test_One;

   ----------------------------------------------------------------------------
   --  Will raise a constraint error at runtime.
   ----------------------------------------------------------------------------
   Test_Two :
      declare
         Buffer_Two : Buffer_Type_Two
         with Import,
         Convention => Ada,
         Address    => Test_Buffer'Address;
      begin
         Put_Line ("Testing type two");
         for I in Natural range 1 .. 16 loop
            Put_Line ("Test: " & Buffer_Two (I)'Image);
         end loop;
      exception
         when Constraint_Error =>
            Put_Line ("Constraint error encountered.");
      end Test_Two;

end Main;

这是我用来编译此示例的项目文件:

project Test is

   for Object_Dir use "obj";
   for Exec_Dir use "build";
   for Create_Missing_Dirs use "True";

   for Languages use ("Ada");

   package Builder is
      for Executable ("main.adb") use "test";
   end Builder;

   for Main use ("main.adb");

   package Compiler is
      for Default_Switches ("Ada") use (
        "-gnat2012",
        "-gnatwadehl",
        "-gnatVa",
        "-gnaty3abcdefhiklmnoprstux"
      );
   end Compiler;

   for Runtime ("Ada") use "zfp";
end Test;

我似乎无法在RM中找到任何能够表明发生这种情况的原因。

编辑:下面的西蒙·赖特(Simon Wright)弄清楚了为什么会这样。我的幼稚理解是,在指定的内存地址上覆盖的Buffer_Type_Two实例会将该位置的内存解释为12位值的序列。似乎并非如此。似乎编译器正在将类型的大小四舍五入至16位,然后从数组读取的16位值不符合12位类型时,将引发Constraint_Error

[如果有人能想到一种更好的方法来以顺序方式从内存中的位置读取12位值的序列,我将不胜感激,谢谢。

ada gnat
3个回答
1
投票

考虑到编译警告,该代码实际上不值得工作...

31.          Buffer_One : Buffer_Type_One
32.            with Import,
33.              Convention => Ada,
34.              Address    => Test_Buffer'Address;
                 |
    >>> warning: specified address for "Buffer_One" may be inconsistent with alignment
    >>> warning: program execution may be erroneous (RM 13.3(27))
    >>> warning: alignment of "Buffer_One" is 2
    >>> warning: alignment of "Test_Buffer" is 1

49.          Buffer_Two : Buffer_Type_Two
50.            with Import,
51.              Convention => Ada,
52.              Address    => Test_Buffer'Address;
                 |
    >>> warning: specified address for "Buffer_Two" may be inconsistent with alignment
    >>> warning: program execution may be erroneous (RM 13.3(27))
    >>> warning: alignment of "Buffer_Two" is 2
    >>> warning: alignment of "Test_Buffer" is 1

但是这不是问题,因为它发生了:Type_Two是mod 2 ** 12,即mod 4096,但Buffer_Two(1)中的值为16#2020#(两个空格字符),十进制为8224。

“但是为什么存储的值不被自动屏蔽为我要求的12位?”你说。出于效率的考虑,“对象的大小不一定与对象类型的大小相同” GNAT RM 4.43,并且GNAT希望16位字的顶部的剩余4位用于为零。一个Type_Two类型的值本身占用(具有’Object_Size个)16位。您可以通过在记录中包含Type_Two字段并指定其布局来获得所需的12位大小,但这确实增加了复杂性。

没有-gnatVa(打开所有有效性检查选项)在这里没有检测到问题。


1
投票

使用最新的GNAT,您可以通过将Buffer_Type_Two定义为]来实现所需的行为>

type Buffer_Type_Two is array (1 .. 128) of Type_Two
  with Pack;

[ARM 13.2(9)警告这可能无法满足您对13位值的要求(不过,最近的GNAT可以做到)。

一种替代方法是

type Buffer_Type_Two is array (1 .. 128) of Type_Two
  with Component_Size => 12;

结果是

...
Testing type two
Test:  32
Test:  514
Test:  32
Test:  514
...

对于13位,使用任何一种方法,

...
Testing type two
Test:  32
Test:  257
Test:  2056
Test:  64
Test:  514
Test:  4112
Test:  128
Test:  1028
Test:  32
...

但是,对于嵌入式目标,您将需要使用-full-运行时系统;对于其他人,如上面@egilhh所指出的,

ajxs.adb:14:09: packing of 12-bit components not supported by configuration

0
投票

[16#2020#的物理值不在Type_Two的范围内,因此-gnatVa预期为Constraint_Error

© www.soinside.com 2019 - 2024. All rights reserved.