Implement virtual dispatch. :-) This is self-consistent with clang,
but not yet necessarily perfectly consistent with gcc. Also addressed
Doug and John's comments.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80137 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp
index 3efda13..5405ac0 100644
--- a/lib/CodeGen/CGCXX.cpp
+++ b/lib/CodeGen/CGCXX.cpp
@@ -200,15 +200,9 @@
const FunctionProtoType *FPT = MD->getType()->getAsFunctionProtoType();
- if (MD->isVirtual()) {
- ErrorUnsupported(CE, "virtual dispatch");
- }
-
const llvm::Type *Ty =
CGM.getTypes().GetFunctionType(CGM.getTypes().getFunctionInfo(MD),
FPT->isVariadic());
- llvm::Constant *Callee = CGM.GetAddrOfFunction(GlobalDecl(MD), Ty);
-
llvm::Value *This;
if (ME->isArrow())
@@ -217,6 +211,13 @@
LValue BaseLV = EmitLValue(ME->getBase());
This = BaseLV.getAddress();
}
+
+ llvm::Value *Callee;
+ // FIXME: Someone needs to keep track of the qualifications.
+ if (MD->isVirtual() /* && !ME->NotQualified() */)
+ Callee = BuildVirtualCall(MD, This, Ty);
+ else
+ Callee = CGM.GetAddrOfFunction(GlobalDecl(MD), Ty);
return EmitCXXMemberCall(MD, Callee, This,
CE->arg_begin(), CE->arg_end());
@@ -826,6 +827,10 @@
}
class VtableBuilder {
+public:
+ /// Index_t - Vtable index type.
+ typedef uint64_t Index_t;
+private:
std::vector<llvm::Constant *> &methods;
llvm::Type *Ptr8Ty;
/// Class - The most derived class that this vtable is being built for.
@@ -840,7 +845,7 @@
CodeGenModule &CGM; // Per-module state.
/// Index - Maps a method decl into a vtable index. Useful for virtual
/// dispatch codegen.
- llvm::DenseMap<const CXXMethodDecl *, int32_t> Index;
+ llvm::DenseMap<const CXXMethodDecl *, Index_t> Index;
typedef CXXRecordDecl::method_iterator method_iter;
public:
VtableBuilder(std::vector<llvm::Constant *> &meth,
@@ -852,6 +857,7 @@
Ptr8Ty = llvm::PointerType::get(llvm::Type::getInt8Ty(VMContext), 0);
}
+ llvm::DenseMap<const CXXMethodDecl *, Index_t> &getIndex() { return Index; }
llvm::Constant *GenerateVcall(const CXXMethodDecl *MD,
const CXXRecordDecl *RD,
bool VBoundary,
@@ -932,17 +938,7 @@
SeenVBase.clear();
}
- inline uint32_t nottoobig(uint64_t t) {
- assert(t < (uint32_t)-1ULL || "vtable too big");
- return t;
- }
-#if 0
- inline uint32_t nottoobig(uint32_t t) {
- return t;
- }
-#endif
-
- void AddMethod(const CXXMethodDecl *MD, int32_t FirstIndex) {
+ void AddMethod(const CXXMethodDecl *MD, Index_t AddressPoint) {
typedef CXXMethodDecl::method_iterator meth_iter;
llvm::Constant *m;
@@ -963,34 +959,34 @@
om = CGM.GetAddrOfFunction(GlobalDecl(OMD), Ptr8Ty);
om = llvm::ConstantExpr::getBitCast(om, Ptr8Ty);
- for (int32_t i = FirstIndex, e = nottoobig(methods.size()); i != e; ++i) {
+ for (Index_t i = AddressPoint, e = methods.size();
+ i != e; ++i) {
// FIXME: begin_overridden_methods might be too lax, covariance */
if (methods[i] == om) {
methods[i] = m;
- Index[MD] = i;
+ Index[MD] = i - AddressPoint;
return;
}
}
}
// else allocate a new slot.
- Index[MD] = methods.size();
+ Index[MD] = methods.size() - AddressPoint;
methods.push_back(m);
}
- void GenerateMethods(const CXXRecordDecl *RD, int32_t FirstIndex) {
+ void GenerateMethods(const CXXRecordDecl *RD, Index_t AddressPoint) {
for (method_iter mi = RD->method_begin(), me = RD->method_end(); mi != me;
++mi)
if (mi->isVirtual())
- AddMethod(*mi, FirstIndex);
+ AddMethod(*mi, AddressPoint);
}
int64_t GenerateVtableForBase(const CXXRecordDecl *RD,
bool forPrimary,
bool VBoundary,
int64_t Offset,
- bool ForVirtualBase,
- int32_t FirstIndex) {
+ bool ForVirtualBase) {
llvm::Constant *m = llvm::Constant::getNullValue(Ptr8Ty);
int64_t AddressPoint=0;
@@ -1023,8 +1019,9 @@
if (PrimaryBaseWasVirtual)
IndirectPrimary.insert(PrimaryBase);
Top = false;
- AddressPoint = GenerateVtableForBase(PrimaryBase, true, PrimaryBaseWasVirtual|VBoundary,
- Offset, PrimaryBaseWasVirtual, FirstIndex);
+ AddressPoint = GenerateVtableForBase(PrimaryBase, true,
+ PrimaryBaseWasVirtual|VBoundary,
+ Offset, PrimaryBaseWasVirtual);
}
if (Top) {
@@ -1041,7 +1038,7 @@
}
// And add the virtuals for the class to the primary vtable.
- GenerateMethods(RD, FirstIndex);
+ GenerateMethods(RD, AddressPoint);
// and then the non-virtual bases.
for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(),
@@ -1053,8 +1050,7 @@
if (Base != PrimaryBase || PrimaryBaseWasVirtual) {
uint64_t o = Offset + Layout.getBaseClassOffset(Base);
StartNewTable();
- FirstIndex = methods.size();
- GenerateVtableForBase(Base, true, false, o, false, FirstIndex);
+ GenerateVtableForBase(Base, true, false, o, false);
}
}
return AddressPoint;
@@ -1071,8 +1067,7 @@
IndirectPrimary.insert(Base);
StartNewTable();
int64_t BaseOffset = BLayout.getVBaseClassOffset(Base);
- int32_t FirstIndex = methods.size();
- GenerateVtableForBase(Base, false, true, BaseOffset, true, FirstIndex);
+ GenerateVtableForBase(Base, false, true, BaseOffset, true);
}
if (Base->getNumVBases())
GenerateVtableForVBases(Base, Class);
@@ -1080,6 +1075,43 @@
}
};
+class VtableInfo {
+public:
+ typedef VtableBuilder::Index_t Index_t;
+private:
+ CodeGenModule &CGM; // Per-module state.
+ /// Index_t - Vtable index type.
+ typedef llvm::DenseMap<const CXXMethodDecl *, Index_t> ElTy;
+ typedef llvm::DenseMap<const CXXRecordDecl *, ElTy *> MapTy;
+ // FIXME: Move to Context.
+ static MapTy IndexFor;
+public:
+ VtableInfo(CodeGenModule &cgm) : CGM(cgm) { }
+ void register_index(const CXXRecordDecl *RD, const ElTy &e) {
+ assert(IndexFor.find(RD) == IndexFor.end() && "Don't compute vtbl twice");
+ // We own a copy of this, it will go away shortly.
+ new ElTy (e);
+ IndexFor[RD] = new ElTy (e);
+ }
+ Index_t lookup(const CXXMethodDecl *MD) {
+ const CXXRecordDecl *RD = MD->getParent();
+ MapTy::iterator I = IndexFor.find(RD);
+ if (I == IndexFor.end()) {
+ std::vector<llvm::Constant *> methods;
+ VtableBuilder b(methods, RD, CGM);
+ b.GenerateVtableForBase(RD, true, false, 0, false);
+ b.GenerateVtableForVBases(RD, RD);
+ register_index(RD, b.getIndex());
+ I = IndexFor.find(RD);
+ }
+ assert(I->second->find(MD)!=I->second->end() && "Can't find vtable index");
+ return (*I->second)[MD];
+ }
+};
+
+// FIXME: Move to Context.
+VtableInfo::MapTy VtableInfo::IndexFor;
+
llvm::Value *CodeGenFunction::GenerateVtable(const CXXRecordDecl *RD) {
llvm::SmallString<256> OutName;
llvm::raw_svector_ostream Out(OutName);
@@ -1095,7 +1127,7 @@
VtableBuilder b(methods, RD, CGM);
// First comes the vtables for all the non-virtual bases...
- Offset = b.GenerateVtableForBase(RD, true, false, 0, false, 0);
+ Offset = b.GenerateVtableForBase(RD, true, false, 0, false);
// then the vtables for all the virtual bases.
b.GenerateVtableForVBases(RD, RD);
@@ -1112,6 +1144,31 @@
return vtable;
}
+// FIXME: move to Context
+static VtableInfo *vtableinfo;
+
+llvm::Value *
+CodeGenFunction::BuildVirtualCall(const CXXMethodDecl *MD, llvm::Value *&This,
+ const llvm::Type *Ty) {
+ // FIXME: If we know the dynamic type, we don't have to do a virtual dispatch.
+
+ // FIXME: move to Context
+ if (vtableinfo == 0)
+ vtableinfo = new VtableInfo(CGM);
+
+ VtableInfo::Index_t Idx = vtableinfo->lookup(MD);
+
+ Ty = llvm::PointerType::get(Ty, 0);
+ Ty = llvm::PointerType::get(Ty, 0);
+ Ty = llvm::PointerType::get(Ty, 0);
+ llvm::Value *vtbl = Builder.CreateBitCast(This, Ty);
+ vtbl = Builder.CreateLoad(vtbl);
+ llvm::Value *vfn = Builder.CreateConstInBoundsGEP1_64(vtbl,
+ Idx, "vfn");
+ vfn = Builder.CreateLoad(vfn);
+ return vfn;
+}
+
/// EmitClassAggrMemberwiseCopy - This routine generates code to copy a class
/// array of objects from SrcValue to DestValue. Copying can be either a bitwise
/// copy or via a copy constructor call.