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

Passing string constant or literal to GCC built-ins in Ada

我之前在 GNAT 中使用过一些内在函数,但是在尝试传入 Chars_Ptr:

时出现 __builtin_cpu_is 错误
error: parameter to builtin must be a string constant or literal

我也试过直接插入“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;

我一直在查看的参考资料:

尝试如下所示的 Target 怎么样

目标:常量 Interfaces.C.Char_Ptr := Interfaces.C.To_C ("amd");

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

错误是由 GCC 后端抛出的(参见 here)。内置仅接受字符串文字。这从内置的等效 C 签名中不清楚。等效的 C 签名表明接受任何指向字符的常量指针:

int __builtin_cpu_is (const char *cpuname)

但是,一个简单的测试表明这是可行的:

#include <stdbool.h>

bool is_amd() {
    return __builtin_cpu_is("amd") != 0;
}

但事实并非如此:

#include <stdbool.h>

bool is_cpu(const char *cpuname) {
    return __builtin_cpu_is(cpuname) != 0;
}

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

$ 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;
}

现在,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");

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

AMD : aliased constant String := "amd"

然后

Is_CPU (AMD'Access);

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

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

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

但是这个签名与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;

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

Eric 找到了可行的解决方案:

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;