将字符串常量或文字传递给 Ada 中的 GCC 内置函数

MtR*_*oad 6 ada

__builtin_cpu_is我之前在 GNAT 中使用过一些内在函数,但在尝试传递以下内容时出现错误Chars_Ptr

error: parameter to builtin must be a string constant or literal
Run Code Online (Sandbox Code Playgroud)

我还尝试直接插入“amd”目标参数,但这不起作用。

with Ada.Text_IO;
with Interfaces.C.Strings;

procedure Intrinsics is
   procedure CPU_Init;
   pragma Import (Intrinsic, CPU_Init, "__builtin_cpu_init");

   function Is_CPU (CPU_Name : Interfaces.C.Strings.chars_ptr) return Interfaces.C.Int;
   pragma Import (Intrinsic, Is_CPU, "__builtin_cpu_is");
   
   Target : constant Interfaces.C.Strings.Chars_Ptr := Interfaces.C.Strings.New_String ("amd");
begin
   CPU_Init;

   -- ERROR from the below line, from Is_CPU
   Ada.Text_IO.Put_Line (Interfaces.C.Int'Image (Is_CPU (Target)));
end Intrinsics;
Run Code Online (Sandbox Code Playgroud)

我一直在看的参考资料:

Dee*_*Dee 6

认为您在 Ada 程序中导入 GCC 内在函数时遇到了(当前)限制(至少对于 GCC/GNAT FSF 11.2 版本)。最好的解决方法是在 C 函数中使用字符串文字包装内置/内在函数,然后在 Ada 程序中导入该 C 包装函数。

该错误是由 GCC 后端引发的(请参阅此处)。内置函数仅接受字符串文字。从内置函数的等效 C 签名中并不清楚这一点。等效的 C 签名表明接受任何指向 char 的常量指针:

int __builtin_cpu_is (const char *cpuname)
Run Code Online (Sandbox Code Playgroud)

然而,一个简单的测试表明这是有效的:

#include <stdbool.h>

bool is_amd() {
    return __builtin_cpu_is("amd") != 0;
}
Run Code Online (Sandbox Code Playgroud)

但这并没有:

#include <stdbool.h>

bool is_cpu(const char *cpuname) {
    return __builtin_cpu_is(cpuname) != 0;
}
Run Code Online (Sandbox Code Playgroud)

在编译期间,将分析抽象语法树,并对内置函数的引用与传入的实际参数进行匹配。该实际参数必须是字符串文字(特定的树节点类型)。然后,GCC 会解析/匹配字符串文字。成功后,对语法树中内置函数的调用(作为整体)将被比较(在此处完成)替换。

$ gcc -c is_amd.c --dump-tree-original && cat is_amd.c.005t.original

;; Function is_amd (null)
;; enabled by -tree-original


{
  return __cpu_model.__cpu_vendor == 2 ? 1 : 0;
}

Run Code Online (Sandbox Code Playgroud)

现在,GNAT 前端似乎无法在语法树中生成与内置解析器所期望的节点相匹配的确切节点(或节点模式)。这可能是因为内置函数声明的签名以及 Ada 对字符串值和字符串指针进行了明确区分。

GNAT 前端将绑定__builtin_cpu_is与 GCC 后端内部声明的签名进行比较,并得出结论:该cpuname参数必须是指向字符串的常量指针。所以,像这样:

function Is_CPU (CPU_Name : access constant String) return Integer;
pragma Import (Intrinsic, Is_CPU, "__builtin_cpu_is");
Run Code Online (Sandbox Code Playgroud)

但是,使用此签名时,不能直接传递字符串文字;你必须使用一些间接的:

AMD : aliased constant String := "amd"
Run Code Online (Sandbox Code Playgroud)

进而

Is_CPU (AMD'Access);
Run Code Online (Sandbox Code Playgroud)

在 GNAT 前端将语法树移交给 GCC 后端之前,这种间接寻址(据我所知)已被保留;GNAT 不会“内联”字符串文字(即:不会删除间接寻址;我认为这实际上是一件好事,因为您通常不希望将常量字符串内联到函数调用中:多个函数可能会引用该字符串如果字符串很大,内联的效果可能会导致程序大小显着增加)。

另一方面,如果你想直接在 Ada 中传递字符串文字,那么你需要一个类似于

function Is_CPU (CPU_Name : String) return Integer;
pragma Import (Intrinsic, Is_CPU, "__builtin_cpu_is");
Run Code Online (Sandbox Code Playgroud)

然而,这个签名与 GCC 后端声明的签名冲突。此外,GNAT 前端会抱怨字符串文字无法通过副本传递(后端可能需要接受和识别调用)。

因此,我猜想必须将一些用于处理带有字符串参数的 GCC 内置函数的额外逻辑添加到 GNAT 前端,才能使其正常工作并允许编译如下内容:

function Is_AMD return Boolean is

    function Is_CPU (CPU_Name : String) return Integer;
    pragma Import (Intrinsic, Is_CPU, "__builtin_cpu_is");

begin
    return Is_CPU ("amd") /= 0;
end Is_AMD;
Run Code Online (Sandbox Code Playgroud)

在那之前,将内在函数用字符串文字包装在单独的 C 函数中(如is_amd()上面的示例),然后在 Ada 程序中导入此 C 包装函数将是可行的方法。


Max*_*nik 2

埃里克找到了一个可行的解决方案:

with Ada.Unchecked_Conversion;
with Ada.Text_IO;
with Interfaces.C.Strings;

procedure Main is
   procedure CPU_Init;
   pragma Import (Intrinsic, CPU_Init, "__builtin_cpu_init");

   function Is_CPU (CPU_Name : Interfaces.C.Strings.chars_ptr) return Integer;
   pragma Import (Intrinsic, Is_CPU, "__builtin_cpu_is");

   function To_Chars_Ptr is
     new Ada.Unchecked_Conversion (String, Interfaces.C.Strings.chars_ptr);

begin
   CPU_Init;
   Ada.Text_IO.Put_Line (Integer'Image (Is_CPU (To_Chars_Ptr ("intel"))));
end;
Run Code Online (Sandbox Code Playgroud)