c - 有没有一种标准的方法来重建降低的结构函数参数?

标签 c clang llvm llvm-ir abi

我有一个结构类型:

typedef struct boundptr {
  uint8_t *ptr;
  size_t size;
} boundptr;

我想捕获该类型函数的所有参数。例如。在这个函数中:

boundptr sample_function_stub(boundptr lp, boundptr lp2);

在我的 64 位机器上,Clang 将该签名翻译成:

define { i8*, i64 } @sample_function_stub(i8* %lp.coerce0, i64 %lp.coerce1, i8* %lp2.coerce0, i64 %lp2.coerce1) #0 {

问题:

有没有更好的方法来重构这些论点?

是否可以禁止此类参数降低,同时为外部调用保持相同的 ABI?

更多上下文:

所以在 LLVM IR 中,我猜,根据平台 ABI,编译器将结构分解为单独的字段(这不是最坏的情况,请参阅 1)。顺便说一句,它在函数体后面重建了原来的两个参数 lplp2

现在对于我的分析,我想从这 4(lp.coerce0 , lp.coerce1, lp2.coerce0lp2.coerce1)。在这种情况下,我可能可以依赖名称(.coerce0 表示第一个字段,.coerce1 - 第二个)。

我不喜欢这种方法:

  • 我不确定 Clang 是否会在以后的版本中保留此约定
  • 这当然取决于 ABI,因此在另一个平台上可能会有不同的 segmentation 。

另一方面,我不能在函数开头使用重构代码,因为我可能会将它与某些局部变量的用户代码混淆。


我使用基于 LLVM 3.4.2 的 Clang 3.4.2 作为目标 x86_64-pc-linux-gnu

附言这是 another example ,展示了 Clang 可以多么疯狂地混淆函数参数。

最佳答案

我假设您没有使用 O0 进行编译。 AFAIK,当您不优化代码时,clang 会重新组装原始类型。 Clang 分解您的结构以通过寄存器(至少在 x86 上)将它们传递给被调用的函数。正如您所说,这取决于所使用的 ABI。

这是您的用例中的一个虚拟示例:

#include <cstddef>

typedef struct boundptr {
  void *ptr;
  size_t size;
} boundptr;

boundptr foo(boundptr ptr1, boundptr ptr2) { return {ptr1.ptr, ptr2.size}; }

int main() {
  boundptr p1, p2;
  boundptr p3 = foo(p1, p2);
  return 0;
}

clang -O0 -std=c++11 -emit-llvm -S -c test.cpp 编译生成 foo:

define { i8*, i64 } @_Z3foo8boundptrS_(i8* %ptr1.coerce0, i64 %ptr1.coerce1, i8* %ptr2.coerce0, i64 %ptr2.coerce1) #0 {
  %1 = alloca %struct.boundptr, align 8
  %ptr1 = alloca %struct.boundptr, align 8
  %ptr2 = alloca %struct.boundptr, align 8
  %2 = bitcast %struct.boundptr* %ptr1 to { i8*, i64 }*
  %3 = getelementptr { i8*, i64 }, { i8*, i64 }* %2, i32 0, i32 0
  store i8** %ptr1.coerce0, i8** %3
  %4 = getelementptr { i8*, i64 }, { i8*, i64 }* %2, i32 0, i32 1
  store i64 %ptr1.coerce1, i64* %4
  %5 = bitcast %struct.boundptr* %ptr2 to { i8*, i64 }*
  %6 = getelementptr { i8*, i64 }, { i8*, i64 }* %5, i32 0, i32 0
  store i8** %ptr2.coerce0, i8** %6
  %7 = getelementptr { i8**, i64 }, { i8**, i64 }* %5, i32 0, i32 1
  store i64 %ptr2.coerce1, i64* %7
  %8 = getelementptr inbounds %struct.boundptr, %struct.boundptr* %1, i32 0, i32 0
  %9 = getelementptr inbounds %struct.boundptr, %struct.boundptr* %ptr1, i32 0, i32 0
  %10 = load i8*, i8** %9, align 8
  store i8* %10, i8** %8, align 8
  %11 = getelementptr inbounds %struct.boundptr, %struct.boundptr* %1, i32 0, i32 1
  %12 = getelementptr inbounds %struct.boundptr, %struct.boundptr* %ptr2, i32 0, i32 1
  %13 = load i64, i64* %12, align 8
  store i64 %13, i64* %11, align 8
  %14 = bitcast %struct.boundptr* %1 to { i8*, i64 }*
  %15 = load { i8*, i64 }, { i8*, i64 }* %14, align 8
  ret { i8*, i64 } %15
}

boundptr 在被调用的函数栈上重构(这也取决于使用的调用约定)。

现在要找出哪些 boundptr 是您的参数,您可以执行以下操作:

  1. 访问您通行证中的每个 alloca 实例并关注其用户。
  2. 按照 alloca 的转换以及 GEP 指令找到 在 boundptr 上存储指令。
  3. 检查要存储的值。如果它们是您的函数参数并且匹配类型和名称,您就找到了重新组装的 boundptr

当然,您可以从函数参数开始,以相反的方式进行。

这是 future 的证明吗?不,绝对不是。 Clang/LLVM 并不是为了保持向后兼容性而设计的。对于兼容性,ABI 很重要。

缺点:您必须在代码生成后尽早将您的传递给优化器。甚至 01 也会删除这些 boundptr 的堆栈分配。因此,您必须修改 clang 以在优化期间执行您的传递,并且您不能使其成为独立传递(例如,由 opt 使用)。

更好的解决方案: 由于必须以某种方式修改 clang,因此您可以添加元数据来标识您的 boundptr 类型的参数。因此,您可以将 boundptr 的片段“打包”在一起,以将它们标识为 boundptr。这将在优化器中存活下来。

关于c - 有没有一种标准的方法来重建降低的结构函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35062429/

相关文章:

objective-c - 新的 Apple LLVM 4.0 "default synthesize"功能如何运作?

无法使定义的数字起作用

c - 创建AVL树时访问冲突异常

c - 如何从 2 个结构指针访问元素?

objective-c - "-Weverything"产生 "Comparing floating point with == or != is unsafe"

haskell - 如何使用GHC编译器将*.hs编译为llvm ir (*.ll)?

c - scanf 的段错误

c - 在 OS X 上使用 -m32 编译 union 的冗余大小

c++ - 在macOS上编译gpsim会导致 “error: ' long long type-name'无效”

xcode - 使用 LLVM/Clang 忽略特定文件中的所有警告