Use opaque [N x i8*] for vtables

This fixes the forward-referencing issue #1741.
This commit is contained in:
Martin 2016-11-05 19:14:46 +01:00
parent e1394fad5f
commit 16d15e01cb
8 changed files with 46 additions and 131 deletions

View file

@ -390,7 +390,6 @@ LLValue *DtoVirtualFunctionPointer(DValue *inst, FuncDeclaration *fdecl,
LLValue *funcval = vthis;
// get the vtbl for objects
stripModifiers(inst->type->toBasetype())->ctype->isClass()->getVtblType(true);
funcval = DtoGEPi(funcval, 0, 0);
// load vtbl ptr
funcval = DtoLoad(funcval);
@ -398,12 +397,12 @@ LLValue *DtoVirtualFunctionPointer(DValue *inst, FuncDeclaration *fdecl,
std::string vtblname = name;
vtblname.append("@vtbl");
funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, vtblname.c_str());
// load funcptr
// load opaque pointer
funcval = DtoAlignedLoad(funcval);
IF_LOG Logger::cout() << "funcval: " << *funcval << '\n';
// cast to final funcptr type
// cast to funcptr type
funcval = DtoBitCast(funcval, getPtrToType(DtoFunctionType(fdecl)));
// postpone naming until after casting to get the name in call instructions

View file

@ -2664,13 +2664,12 @@ public:
LLValue *val = DtoRVal(ex);
// Get and load vtbl pointer.
stripModifiers(t)->ctype->isClass()->getVtblType(true);
llvm::Value *vtbl = DtoLoad(DtoGEPi(val, 0, 0));
// TypeInfo ptr is first vtbl entry.
llvm::Value *typinf = DtoGEPi(vtbl, 0, 0);
Type *resultType = Type::typeinfoclass->type;
Type *resultType;
if (static_cast<TypeClass *>(t)->sym->isInterfaceDeclaration()) {
// For interfaces, the first entry in the vtbl is actually a pointer
// to an Interface instance, which has the type info as its first
@ -2678,6 +2677,9 @@ public:
resultType = Type::typeinfointerface->type;
typinf = DtoLoad(
DtoBitCast(typinf, DtoType(resultType->pointerTo()->pointerTo())));
} else {
resultType = Type::typeinfoclass->type;
typinf = DtoBitCast(typinf, DtoType(resultType->pointerTo()));
}
result = new DLValue(resultType, typinf);

View file

@ -49,7 +49,7 @@ LLGlobalVariable *IrAggr::getVtblSymbol() {
// create the vtblZ symbol
auto initname = getMangledVTableSymbolName(aggrdecl);
LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtblType(false);
LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtblType();
vtbl =
getOrCreateGlobal(aggrdecl->loc, gIR->module, vtblTy, true,
@ -165,11 +165,13 @@ LLConstant *IrAggr::getVtblInit() {
std::vector<llvm::Constant *> constants;
constants.reserve(cd->vtbl.dim);
const auto voidPtrType = getVoidPtrType();
// start with the classinfo
llvm::Constant *c;
if (!cd->isCPPclass()) {
c = getClassInfoSymbol();
c = DtoBitCast(c, DtoType(Type::typeinfoclass->type));
c = DtoBitCast(c, voidPtrType);
constants.push_back(c);
}
@ -220,26 +222,12 @@ LLConstant *IrAggr::getVtblInit() {
}
}
}
constants.push_back(c);
constants.push_back(DtoBitCast(c, voidPtrType));
}
// build the constant struct
LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtblType(true);
#ifndef NDEBUG
size_t nc = constants.size();
for (size_t i = 0; i < nc; ++i) {
if (constants[i]->getType() != vtblTy->getContainedType(i)) {
llvm::errs() << "type mismatch for entry # " << i
<< " in vtbl initializer\n";
constants[i]->getType()->dump();
vtblTy->getContainedType(i)->dump();
}
}
#endif
constVtbl = LLConstantStruct::get(isaStruct(vtblTy), constants);
// build the constant array
LLArrayType *vtblTy = LLArrayType::get(voidPtrType, constants.size());
constVtbl = LLConstantArray::get(vtblTy, constants);
return constVtbl;
}
@ -277,6 +265,8 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance,
std::vector<llvm::Constant *> constants;
constants.reserve(vtbl_array.dim);
const auto voidPtrTy = getVoidPtrType();
if (!b->sym->isCPPinterface()) { // skip interface info for CPP interfaces
// index into the interfaces array
llvm::Constant *idxs[2] = {DtoConstSize_t(0),
@ -289,7 +279,7 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance,
#endif
interfaceInfosZ, idxs, true);
constants.push_back(c);
constants.push_back(DtoBitCast(c, voidPtrTy));
}
// Thunk prefix
@ -306,7 +296,7 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance,
// FIXME
// why is this null?
// happens for mini/s.d
constants.push_back(getNullValue(getVoidPtrType()));
constants.push_back(getNullValue(voidPtrTy));
continue;
}
@ -327,7 +317,7 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance,
if (fd->interfaceVirtual)
thunkOffset -= fd->interfaceVirtual->offset;
if (thunkOffset == 0) {
constants.push_back(irFunc->func);
constants.push_back(DtoBitCast(irFunc->func, voidPtrTy));
continue;
}
@ -434,12 +424,12 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance,
gIR->funcGenStates.pop_back();
}
constants.push_back(thunk);
constants.push_back(DtoBitCast(thunk, voidPtrTy));
}
// build the vtbl constant
llvm::Constant *vtbl_constant =
LLConstantStruct::getAnon(gIR->context(), constants, false);
llvm::Constant *vtbl_constant = LLConstantArray::get(
LLArrayType::get(voidPtrTy, constants.size()), constants);
std::string mangledName("_D");
mangledName.append(mangle(cd));
@ -526,7 +516,8 @@ LLConstant *IrAggr::getClassInfoInterfaces() {
assert(itv != interfaceVtblMap.end() && "interface vtbl not found");
vtb = itv->second;
vtb = DtoBitCast(vtb, voidptrptr_type);
vtb = DtoConstSlice(DtoConstSize_t(itc->getVtblSize()), vtb);
auto vtblSize = itc->getVtblType()->getNumContainedTypes();
vtb = DtoConstSlice(DtoConstSize_t(vtblSize), vtb);
}
// offset

View file

@ -25,10 +25,7 @@
IrTypeClass::IrTypeClass(ClassDeclaration *cd)
: IrTypeAggr(cd), cd(cd), tc(static_cast<TypeClass *>(cd->type)) {
std::string vtbl_name(cd->toPrettyChars());
vtbl_name.append(".__vtbl");
vtbl_type = LLStructType::create(gIR->context(), vtbl_name);
vtbl_size = cd->vtbl.dim;
vtbl_type = LLArrayType::get(getVoidPtrType(), cd->vtbl.dim);
}
void IrTypeClass::addClassData(AggrTypeBuilder &builder,
@ -52,15 +49,10 @@ void IrTypeClass::addClassData(AggrTypeBuilder &builder,
IF_LOG Logger::println("Adding interface vtbl for %s",
b->sym->toPrettyChars());
FuncDeclarations arr;
b->fillVtbl(cd, &arr, currCd == cd);
// add to the interface map
addInterfaceToMap(b->sym, builder.currentFieldIndex());
Type* first = b->sym->isCPPinterface() ? nullptr : interfacePtrType;
const auto ivtblType =
llvm::StructType::get(gIR->context(), buildVtblType(first, &arr));
builder.addType(llvm::PointerType::get(ivtblType, 0), Target::ptrsize);
auto vtblTy = LLArrayType::get(getVoidPtrType(), b->sym->vtbl.dim);
builder.addType(llvm::PointerType::get(vtblTy, 0), Target::ptrsize);
++num_interface_vtbls;
}
@ -122,83 +114,10 @@ IrTypeClass *IrTypeClass::get(ClassDeclaration *cd) {
return t;
}
std::vector<llvm::Type *>
IrTypeClass::buildVtblType(Type *first, FuncDeclarations *vtbl_array) {
IF_LOG Logger::println("Building vtbl type for class %s",
cd->toPrettyChars());
LOG_SCOPE;
std::vector<llvm::Type *> types;
types.reserve(vtbl_array->dim);
auto I = vtbl_array->begin();
// first comes the classinfo for D interfaces
if (first) {
types.push_back(DtoType(first));
++I;
}
// then come the functions
for (auto E = vtbl_array->end(); I != E; ++I) {
FuncDeclaration *fd = *I;
if (fd == nullptr) {
// FIXME: This stems from the ancient D1 days can it still happen?
types.push_back(getVoidPtrType());
continue;
}
IF_LOG Logger::println("Adding type of %s", fd->toPrettyChars());
// If inferring return type and semantic3 has not been run, do it now.
// This pops up in some other places in the frontend as well, however
// it is probably a bug that it still occurs that late.
if (!fd->type->nextOf() && fd->inferRetType) {
Logger::println("Running late functionSemantic to infer return type.");
TemplateInstance *spec = fd->isSpeculative();
unsigned int olderrs = global.errors;
fd->functionSemantic();
if (spec && global.errors != olderrs) {
spec->errors = global.errors - olderrs;
}
}
if (!fd->type->nextOf()) {
// Return type of the function has not been inferred. This seems to
// happen with virtual functions and is probably a frontend bug.
IF_LOG Logger::println("Broken function type, semanticRun: %d",
fd->semanticRun);
types.push_back(getVoidPtrType());
continue;
}
types.push_back(getPtrToType(DtoFunctionType(fd)));
}
return types;
}
llvm::Type *IrTypeClass::getLLType() { return llvm::PointerType::get(type, 0); }
llvm::Type *IrTypeClass::getMemoryLLType() { return type; }
llvm::StructType *IrTypeClass::getVtblType(bool notOpaque) {
if (notOpaque && vtbl_type->isOpaque()) {
FuncDeclarations vtbl;
vtbl.reserve(cd->vtbl.dim);
if (!cd->isCPPclass())
vtbl.push(nullptr);
for (size_t i = cd->vtblOffset(); i < cd->vtbl.dim; ++i) {
FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration();
assert(fd);
vtbl.push(fd);
}
Type* first = cd->isCPPclass() ? nullptr : Type::typeinfoclass->type;
vtbl_type->setBody(buildVtblType(first, &vtbl));
}
return vtbl_type;
}
size_t IrTypeClass::getInterfaceIndex(ClassDeclaration *inter) {
auto it = interfaceMap.find(inter);
if (it == interfaceMap.end()) {

View file

@ -37,16 +37,13 @@ public:
llvm::Type *getMemoryLLType();
/// Returns the vtable type for this class.
llvm::StructType *getVtblType(bool notOpaque);
llvm::ArrayType *getVtblType() { return vtbl_type; }
/// Get index to interface implementation.
/// Returns the index of a specific interface implementation in this
/// class or ~0 if not found.
size_t getInterfaceIndex(ClassDeclaration *inter);
/// Returns the total number of pointers in the vtable.
unsigned getVtblSize() { return vtbl_size; }
/// Returns the number of interface implementations (vtables) in this
/// class.
unsigned getNumInterfaceVtbls() { return num_interface_vtbls; }
@ -61,10 +58,7 @@ protected:
TypeClass *tc = nullptr;
/// Vtable type.
llvm::StructType *vtbl_type = nullptr;
/// Number of pointers in vtable.
unsigned vtbl_size = 0;
llvm::ArrayType *vtbl_type = nullptr;
/// Number of interface implementations (vtables) in this class.
unsigned num_interface_vtbls = 0;
@ -78,13 +72,6 @@ protected:
//////////////////////////////////////////////////////////////////////////
/// Builds a vtable type given the type of the first entry and an array
/// of all entries.
/// If first is nullptr for C++ interfaces, the vtbl_array will be added
/// as is without replacing the first entry.
std::vector<llvm::Type *> buildVtblType(Type *first,
FuncDeclarations *vtbl_array);
/// Adds the data members for the given class to the type builder, including
/// those inherited from base classes/interfaces.
void addClassData(AggrTypeBuilder &builder, ClassDeclaration *currCd);

View file

@ -1,6 +1,7 @@
// Tests in-place construction of structs returned by inline assembly (issue #1823).
// Target Win64 for simplicity (e.g., 4x32-bit struct not returned in memory for non-Windows x64).
// REQUIRES: target_X86
// RUN: %ldc -mtriple=x86_64-pc-windows-msvc -c -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
import ldc.llvmasm;

13
tests/compilable/gh1741.d Normal file
View file

@ -0,0 +1,13 @@
// Explicitly target Linux x86_64.
// REQUIRES: target_X86
// RUN: %ldc -mtriple=x86_64-pc-linux-gnu -c %s %S/inputs/gh1741b.d
struct S
{
C c;
}
class C
{
@property s() { return S(); }
}

View file

@ -0,0 +1,3 @@
import gh1741;
S s;