我有一个结构类型:
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)。顺便说一句,它稍后在函数体中重建原始的两个参数lp
和lp2
。
现在,对于我的分析,我希望完全从这4个中lp
,lp2
,{{1}完整地获取这两个参数lp.coerce0
和lp.coerce1
和lp2.coerce0
)。在这种情况下,我可能依赖于名称(lp2.coerce1
表示第一个字段,.coerce0
- 秒)。
我不喜欢这种做法:
另一方面,我不能在函数的开头使用重构代码,因为我可能会将它与局部变量的某些用户代码混淆。
我使用基于LLVM .coerce1
的Clang 3.4.2
作为目标3.4.2
。
P.S。这是another example,显示了Clang如何能够搞乱函数参数。
答案 0 :(得分:1)
我假设您正在编译而不是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
是您的参数,您可以执行以下操作:
alloca
,然后关注其用户。 alloca
的演员表以及GEP说明查找
在boundptr
上存储说明。 boundptr
。当然,你可以从函数参数开始反过来做。
这是未来证明吗?没有明确的否定。 Clang / LLVM不是为了保持向后兼容性而设计的。为了兼容性,ABI很重要。
缺点:你必须在代码生成之后很早就进入优化器。即使01
也会删除boundptr
的这些堆栈分配。因此,您必须修改clang
以在优化期间执行传递,而不能将其作为独立传递(例如opt
使用)。
更好的解决方案:
由于必须以某种方式修改clang,因此您可以添加标识boundptr
类型参数的元数据。因此,您可以将boundptr
的片段“打包”在一起,将其标识为boundptr
。这将在优化器中存活。