SPARK Ada使用GNATProve假设GCC内在函数的后置条件

SPARK Ada使用GNATProve假设GCC内在函数的后置条件,ada,spark-ada,Ada,Spark Ada,我想在SPARK_模式下创建一个函数,它利用GNAT GCC的内在函数“_builtin_ctzll” 我想假设后置条件为真,因为它是文档中暗示的尾随零数的定义。然而,我不确定如何做到这一点,因为我已经阅读了大量文档,并尝试使用“pragma-aspect”。我对Ada/SPARK比较陌生,正在使用GNAT Community 2020。有人能帮我解决这个问题,让gnatprove能够证明CTZLL的后条件吗?当我使用Shift\u Right制定内置CTZLL的后条件(合同)时,我能够证明(使

我想在SPARK_模式下创建一个函数,它利用GNAT GCC的内在函数“_builtin_ctzll”


我想假设后置条件为真,因为它是文档中暗示的尾随零数的定义。然而,我不确定如何做到这一点,因为我已经阅读了大量文档,并尝试使用“pragma-aspect”。我对Ada/SPARK比较陌生,正在使用GNAT Community 2020。有人能帮我解决这个问题,让gnatprove能够证明CTZLL的后条件吗?

当我使用
Shift\u Right
制定
内置CTZLL的后条件(合同)时,我能够证明(使用GNAT CE 2020和证明级别1)如果要运行的话,
test.adb
没有运行时错误

注:相关文件:SPARK用户手册,第7.4.5节:

内在广告

pragma Assertion_Policy (Check);

with Interfaces; use Interfaces;

package Intrinsic with SPARK_Mode is

   --  Count Trailing Zeros (long long unsigned).
   
   function CTZLL (X : Unsigned_64) return Natural with
     Pre  => X /= 0,       
     Post => CTZLL'Result in 0 .. Unsigned_64'Size - 1 and
             (for all I in 0 .. CTZLL'Result - 1 =>
                (Shift_Right (X, I) and 2#1#) = 2#0#) and 
             (Shift_Right (X, CTZLL'Result) and 2#1#) = 2#1#;

   --  You could also use aspects (Import, Convention, External_Name).
   pragma Import (Intrinsic, CTZLL, "__builtin_ctzll");
   
end Intrinsic;
test.adb

pragma Assertion_Policy (Check);

with Interfaces; use Interfaces;
with Intrinsic;  use Intrinsic;

procedure Test with SPARK_Mode is
begin
   
   --  Absence of Run-Time Errors (AoRTE) for this program can be proven:
   --  Assert_Failure will not be raised at runtime.
   
   pragma Assert (CTZLL ( 1) = 0);
   pragma Assert (CTZLL ( 2) = 1);
   pragma Assert (CTZLL ( 3) = 0);
   pragma Assert (CTZLL ( 4) = 2);
   pragma Assert (CTZLL ( 5) = 0);
   pragma Assert (CTZLL ( 6) = 1);
   pragma Assert (CTZLL ( 7) = 0);
   pragma Assert (CTZLL ( 8) = 3);
   pragma Assert (CTZLL ( 9) = 0);
   pragma Assert (CTZLL (10) = 1);
      
   pragma Assert (CTZLL (2 ** 63    ) = 63);
   pragma Assert (CTZLL (2 ** 64 - 1) =  0);
   
end Test;
with Ada.Text_IO;         use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Interfaces;          use Interfaces;
with Intrinsic;           use Intrinsic;

procedure Main is
begin
   for K in 1 .. 10 loop
      Put (K, Width => 3);
      Put (K, Width => 9, Base => 2);
      Put (CTZLL (Unsigned_64 (K)), Width => 4);
      New_Line;
   end loop;
end Main;
输出(gnatprove的)


对于那些不熟悉
\uuuuu builtin\u ctzll
:返回x中尾随0位的数量,从最低有效位位置开始。如果x为0,则结果未定义。另见。例如:

main.adb

pragma Assertion_Policy (Check);

with Interfaces; use Interfaces;
with Intrinsic;  use Intrinsic;

procedure Test with SPARK_Mode is
begin
   
   --  Absence of Run-Time Errors (AoRTE) for this program can be proven:
   --  Assert_Failure will not be raised at runtime.
   
   pragma Assert (CTZLL ( 1) = 0);
   pragma Assert (CTZLL ( 2) = 1);
   pragma Assert (CTZLL ( 3) = 0);
   pragma Assert (CTZLL ( 4) = 2);
   pragma Assert (CTZLL ( 5) = 0);
   pragma Assert (CTZLL ( 6) = 1);
   pragma Assert (CTZLL ( 7) = 0);
   pragma Assert (CTZLL ( 8) = 3);
   pragma Assert (CTZLL ( 9) = 0);
   pragma Assert (CTZLL (10) = 1);
      
   pragma Assert (CTZLL (2 ** 63    ) = 63);
   pragma Assert (CTZLL (2 ** 64 - 1) =  0);
   
end Test;
with Ada.Text_IO;         use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Interfaces;          use Interfaces;
with Intrinsic;           use Intrinsic;

procedure Main is
begin
   for K in 1 .. 10 loop
      Put (K, Width => 3);
      Put (K, Width => 9, Base => 2);
      Put (CTZLL (Unsigned_64 (K)), Width => 4);
      New_Line;
   end loop;
end Main;
输出(属于
Main


您无法证明后置条件(
gnatprove
需要查看内在代码)。你能做的就是写一个后置条件,让你能够证明使用
CTZLL
的代码是正确的;并使用其他方式向您证明,
CTZLL
的行为确实符合合同规定。您对后条件的最新更改采纳了我的评论,但该评论似乎尚未发布。这是思想转移吗?!(我可能刚刚忘记发布了!)有没有什么方法可以假设postcondition为真,以证明另一个使用CTZLL的函数?我不知道该怎么做。例如,二进制GCD算法使用CTZLL来大大提高性能。@alfxs如@Simon Wright在其对原始问题的评论中所述,
gnatprove
无法证明导入子程序的后置条件成立,因此假设如果先决条件成立,后置条件将成立(在这种情况下,
X
CTZLL
的结果之间的函数关系将保持iff
X/=0
)。合同是否(在这种情况下为关系)是否足够准确,或者是否以一种有用的方式来证明算法中的其他条件(其中
CTZLL
是算法的一部分),取决于要在算法中验证的条件。@alfxs既然您提到了GCD算法:有一篇很好的博客文章讨论了证明函数的方法使用SPARK的GCD算法的nal行为:。您可能已经找到了它,但我想我提到它是为了完整性。代码在GNAT CE 2020中提供(GNAT Studio>帮助>SPARK>示例>GCD)。
$ ./obj/main
  1     2#1#   0
  2    2#10#   1
  3    2#11#   0
  4   2#100#   2
  5   2#101#   0
  6   2#110#   1
  7   2#111#   0
  8  2#1000#   3
  9  2#1001#   0
 10  2#1010#   1