llvm如何知道成员函数指针是否指向虚函数?

时间:2018-04-19 12:50:17

标签: c++ llvm member-function-pointers

在编写了一些关于成员函数指针和读取Itanium ABI#Member function pointers的代码之后,我已经理解了llvm中成员函数指针的布局。

但令我感到震惊的是如何使用成员函数指针获取函数的地址。我发现无法判断成员函数指针mfptr是指向虚拟成员函数还是普通成员函数。

2 个答案:

答案 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。