Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ Clang—将C头编译为LLVM IR/位代码_C++_C_Clang_Llvm_Llvm Ir - Fatal编程技术网

C++ Clang—将C头编译为LLVM IR/位代码

C++ Clang—将C头编译为LLVM IR/位代码,c++,c,clang,llvm,llvm-ir,C++,C,Clang,Llvm,Llvm Ir,假设我有以下简单的C头文件: // foo1.h typedef int foo; typedef struct { foo a; char const* b; } bar; bar baz(foo*, bar*, ...); 我的目标是获取此文件,并生成一个类似以下内容的LLVM模块: %struct.bar = type { i32, i8* } declare { i32, i8* } @baz(i32*, %struct.bar*, ...) 换句话说,将带有声明的C.h

假设我有以下简单的C头文件:

// foo1.h
typedef int foo;

typedef struct {
  foo a;
  char const* b;
} bar;

bar baz(foo*, bar*, ...);
我的目标是获取此文件,并生成一个类似以下内容的LLVM模块

%struct.bar = type { i32, i8* }
declare { i32, i8* } @baz(i32*, %struct.bar*, ...)
换句话说,将带有声明的C
.h
文件转换为等效的LLVM IR,包括类型解析、宏扩展等

通过Clang生成LLVM IR会生成一个空模块(因为实际上没有使用任何定义):

我的第一反应是转向谷歌,我遇到了两个相关的问题:,和。两人都建议使用
-femit all decls
标志,因此我尝试:

$ clang -cc1 -femit-all-decls -S -emit-llvm foo1.h -o -
; ModuleID = 'foo1.h'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-darwin13.3.0"

!llvm.ident = !{!0}

!0 = metadata !{metadata !"clang version 3.5 (trunk 200156) (llvm/trunk 200155)"}
同样的结果

我也尝试过禁用优化(使用
-O0
-disable llvm optzns
),但这对输出没有影响。使用以下变化确实产生了所需的IR:

// foo2.h
typedef int foo;

typedef struct {
  foo a;
  char const* b;
} bar;

bar baz(foo*, bar*, ...);

void doThings() {
  foo a = 0;
  bar myBar;
  baz(&a, &myBar);
}
然后运行:

$ clang -cc1 -S -emit-llvm foo2.h -o -
; ModuleID = 'foo2.h'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-darwin13.3.0"

%struct.bar = type { i32, i8* }

; Function Attrs: nounwind
define void @doThings() #0 {
entry:
  %a = alloca i32, align 4
  %myBar = alloca %struct.bar, align 8
  %coerce = alloca %struct.bar, align 8
  store i32 0, i32* %a, align 4
  %call = call { i32, i8* } (i32*, %struct.bar*, ...)* @baz(i32* %a, %struct.bar* %myBar)
  %0 = bitcast %struct.bar* %coerce to { i32, i8* }*
  %1 = getelementptr { i32, i8* }* %0, i32 0, i32 0
  %2 = extractvalue { i32, i8* } %call, 0
  store i32 %2, i32* %1, align 1
  %3 = getelementptr { i32, i8* }* %0, i32 0, i32 1
  %4 = extractvalue { i32, i8* } %call, 1
  store i8* %4, i8** %3, align 1
  ret void
}

declare { i32, i8* } @baz(i32*, %struct.bar*, ...) #1

attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = metadata !{metadata !"clang version 3.5 (trunk 200156) (llvm/trunk 200155)"}
除了占位符
doThings
,这正是我希望输出的样子!问题是,这需要1.)使用修改过的标题版本,2.)提前知道内容的类型。这让我想到

为什么? 基本上,我正在为一种使用LLVM生成代码的语言构建一个实现。实现应该只通过指定C头文件和关联的LIB(无手动声明)来支持C互操作,然后编译器将在链接时间之前使用这些文件,以确保函数调用与它们的签名匹配。因此,我将问题缩小为两种可能的解决方案:

  • 将头文件转换为LLVM IR/位代码,这样就可以获得每个函数的类型签名
  • 使用
    libclang
    解析标题,然后从生成的AST查询类型(如果这个问题没有足够的答案,我的“最后手段”)
  • TL;博士
    我需要获取一个C头文件(比如上面的
    foo1.h
    ),在不改变它的情况下,使用Clang生成前面提到的预期LLVM IR,或者,找到另一种方法从C头文件中获取函数签名(最好使用
    libclang
    或构建一个C解析器)

    也许是不太优雅的解决方案,但是要坚持使用
    doThings
    函数的思想,该函数强制编译器发出IR,因为使用了以下定义:

    这种方法的两个问题是,它需要修改标题,并且需要更深入地理解所涉及的类型,以便生成“使用”以放入函数。这两个问题都可以相对简单地克服:

  • 不要直接编译头,而是从包含所有“使用”代码的.c文件中包含它(或者更可能是它的预处理版本,或者多个头)。非常简单:

    // foo.c
    #include "foo.h"
    void doThings(void) {
        ...
    }
    
  • 您不需要详细的类型信息来生成名称的特定用法,将结构实例化与参数相匹配,也不需要像上面的“uses”代码那样复杂。实际上,您不需要自己收集函数签名

    您所需要的只是名称本身的列表,并跟踪它们是用于函数还是用于对象类型。然后,您可以重新定义“uses”函数,如下所示:

    void * doThings(void) {
        typedef void * (*vfun)(void);
        typedef union v { void * o; vfun f; } v;
    
        return (v[]) {
            (v){ .o = &(bar){0} },
            (v){ .f = (vfun)baz },
        };
    }
    
    这大大简化了名称的必要“使用”,可以将其强制转换为统一的函数类型(并使用其指针而不是调用它),也可以将其包装在
    &(
    ){0}
    (不管它是什么,都要实例化它)。这意味着您根本不需要存储实际的类型信息,只需要在标题中提取名称的上下文类型

    (显然,为伪函数和占位符类型提供扩展的唯一名称,这样它们就不会与实际要保留的代码冲突)

  • 这大大简化了解析步骤,因为您只需识别结构/联合或函数声明的上下文,而不需要实际处理周围的信息


    一个简单但不成熟的起点(我可能会使用它,因为我的标准很低:D)可能是:

    • grep浏览
      #include
      指令的标题,这些指令采用带角括号的参数(即,您不希望为其生成声明的已安装标题)
    • 使用此列表创建一个虚拟包含文件夹,其中包含所有必需的包含文件,但为空
    • 对其进行预处理,希望能够简化语法(
      clang-E-I local dummy包括/-D“\uuuu attribute\uuuu(…)=”foo.h>temp/foo\u pp.h
      或类似的东西)
    • grep-for
      struct
      union
      后跟一个名称,
      }
      后跟一个名称,或
      名称(
      ),然后使用这个可笑的简化非解析来构建伪函数中的用法列表,并为.c文件发出代码

    它不会抓住每一种可能性;但经过一点调整和扩展,它实际上可能会处理大量实际的头代码。您可以用专用的简化解析器(专门为查看所需上下文的模式而构建的解析器)来代替它在以后的阶段。

    如果我理解正确,它不会发出IR,直到您实际使用声明的内容。因此,您可以不自己解析,而是更改Clang的行为,以发出IR,即使不使用声明。这可能比自己处理AST更容易。很可能您必须在代码中的某个地方翻转布尔值,而e完全正确,我不知道。@mishr是的,我相信你是对的,修改Clang的源代码并简单地切换开关比使用libclang的AST更容易。但是,依赖这样一个大型项目的定制分支肯定不太实际(因为Clang的每个后续版本都需要修补).更不用说,这个问题是一个开源专业人士提出的
    void * doThings(void) {
        typedef void * (*vfun)(void);
        typedef union v { void * o; vfun f; } v;
    
        return (v[]) {
            (v){ .o = &(bar){0} },
            (v){ .f = (vfun)baz },
        };
    }