我遇到了一个问题,即在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位值的序列,我将不胜感激,谢谢。
考虑到编译警告,该代码实际上不值得工作...
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
(打开所有有效性检查选项)在这里没有检测到问题。
使用最新的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
[16#2020#的物理值不在Type_Two
的范围内,因此-gnatVa预期为Constraint_Error
。