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



我以前在GNAT中使用过一些内部函数,但在尝试传入Chars_Ptr:时,__builtin_cpu_is出现错误

error: parameter to builtin must be a string constant or literal

我还试着把";amd";直接输入target参数,但不起作用。

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;

我一直在看的参考文献:

  • GCC内置
  • 学习Ada,带C接口

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

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

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解析/匹配字符串文本。成功后,对语法树中内置的的调用(作为一个整体(将被一个比较(在此处完成(所取代。

$ 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前端会抱怨字符串文本无法通过副本传递(这可能是后端接受和识别调用所必需的(。

因此,我想必须在GNAT前端添加一些额外的逻辑来处理带有字符串参数的GCC内置程序,这样才能工作并允许编译类似的东西:

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;

试试下面显示的Target怎么样

目标:常量接口.Char_Ptr:=接口.C.To_C("amd"(;

最新更新