在编写了一些关于成员函数指针和读取Itanium ABI#Member function pointers的代码之后,我已经理解了llvm中成员函数指针的布局。
但令我感到震惊的是如何使用成员函数指针获取函数的地址。我发现无法判断成员函数指针mfptr是指向虚拟成员函数还是普通成员函数。
答案 0 :(得分:1)
在您linked的文档中,它说:
对于虚函数,它是1加上虚拟表偏移量(in 函数
在Virtual Table Layout下,它说:
虚拟表中的偏移量由该分配序列和自然ABI大小和对齐
确定
偏移量必须符合函数指针的对齐要求。
在相应的C ABI中指定作为“POD”类型的函数指针的对齐要求。我假设指针与它们的大小对齐,因此指针的地址(以及偏移量)必须是偶数,其最低有效位必须为零。
因此,实现只需检查偏移/指针字段的LSB,并知道当且仅当LSB是一个正在处理虚方法时。
一旦虚拟表中有偏移量,它就会从对象中读取虚拟表指针,并使用成员指针的偏移量从虚拟表中加载函数的实际地址。
implicit val context: ExecutionContext =
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(10))
val seqOfFutures = Seq.fill(700)(Future { callRestApi() })
// This `sequence` uses the same thread pool as the original futures
val sequenceFuture = Future.sequence(seqOfFutures)
在x86_64上,clang确实会为方法指针的“ptr”成员的LSB创建一个检查:
{
"query": {
"bool": {
"must": [
{
"exists": {
"field": "priorTransactionDate"
}
},
{
"script": {
"script": "(doc['transactionDate'].date.millis - doc['priorTransactionDate'].date.millis)/1000/86400 < 365"
}
}
]
}
}
}
我无法分享这个特定示例的godbolt链接,因为我的一个浏览器插件受到干扰,但你可以在https://gcc.godbolt.org上自己玩类似的例子。
答案 1 :(得分:0)
我不确定这是llvm究竟做了什么,但我发现正常成员函数和Itanium ABI的虚函数指针之间的区别通常在记录本身的结构中,就像解释的那样here:
虚函数指针的形式由。指定 特定于处理器的C ++ ABI用于实现。具体而言 64位Itanium共享库构建的案例,一个虚函数 指针条目包含一对组件(每个64位):值 目标GP值和实际功能地址。那是, 而不是一个普通的函数指针,它指向这样一个 双组件描述符,一个虚函数指针条目 描述符。
这是正常函数指针是地址,而虚函数指针是由全局位置偏移(GP)和虚函数覆盖的有效地址构成的描述符。现在我想,记录的大小和某种装饰(如果认为它是你指向的链接中提到的'1')可以将一种类型的指针与其他
修改
我发现另一个关于vtable记录w.r.t的定义的提示。为llvm实现 clang 前端TargetCXXABI
类的虚函数成员。此类公开API(行181),它告诉成员函数的主体是否对齐。
正如PaulR在他的回答中所说,这证实了LSB用于区分正常成员函数和虚拟成员函数的事实。但这不是因为指针大小和对齐 - 最小可寻址单位总是一个字节,所以指针可以是奇数 - 但是因为在Itanium C ++ ABI中,正常成员函数的主体是对齐的,因此它们的地址始终是偶数设计数字。
但情况并非总是如此,实际上在这种方法的实现中,提到了一些结构(例如ARM)将鉴别器存储在this
指针的调整中,而不是在功能地址。
所以这个功能真的与处理器架构有关,除了x86_64的LSB +1的一般规则之外,你应该检查每个的类似Itanium的C ++ ABI。