diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -350,6 +350,9 @@ LANGOPT(RegisterStaticDestructors, 1, 1, "Register C++ static destructors") COMPATIBLE_VALUE_LANGOPT(MaxTokens, 32, 0, "Max number of tokens per TU or 0") +LANGOPT(FuchsiaRelativeCXXABIVTables, 1, 0, + "Whether to use clang's relative C++ ABI " + "for classes with vtables on Fuchsia") #undef LANGOPT #undef COMPATIBLE_LANGOPT diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1325,6 +1325,13 @@ "fno-fine-grained-bitfield-accesses">, Group, Flags<[CC1Option]>, HelpText<"Use large-integer access for consecutive bitfield runs.">; +def fexperimental_fuchsia_relative_cxx_abi_vtables : Flag<["-"], "fexperimental-fuchsia-relative-c++-abi-vtables">, + Group, Flags<[CC1Option]>, + HelpText<"Use the experimental C++ class ABI for classes with virtual tables on Fuchsia">; +def fno_experimental_fuchsia_relative_cxx_abi_vtables : Flag<["-"], "fno-experimental-relative-c++-abi-vtables">, + Group, Flags<[CC1Option]>, + HelpText<"Do not use the experimental C++ class ABI for classes with virtual tables on Fuchsia">; + def flat__namespace : Flag<["-"], "flat_namespace">; def flax_vector_conversions_EQ : Joined<["-"], "flax-vector-conversions=">, Group, HelpText<"Enable implicit vector bit-casts">, Values<"none,integer,all">, Flags<[CC1Option]>; diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -665,6 +665,13 @@ CharUnits PointerWidth = Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); CharUnits OffsetOffset = PointerWidth * OffsetIndex; + + if (Context.getLangOpts().FuchsiaRelativeCXXABIVTables) { + // Add 4 bytes since under the relative vtable ABI, the RTTI info component + // is also a 4 byte offset. + OffsetOffset += CharUnits::fromQuantity(4); + } + return OffsetOffset; } diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -209,6 +209,12 @@ llvm::Value *MemPtr, const MemberPointerType *MPT); + /// Create and set the initializer for the vtable. + virtual void SetVTableInitializer(CodeGenVTables &CGVT, + llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI) = 0; + protected: /// A utility method for computing the offset required for the given /// base-to-derived or derived-to-base member-pointer conversion. @@ -461,11 +467,15 @@ /// Gets the offsets of all the virtual base pointers in a given class. virtual std::vector getVBPtrOffsets(const CXXRecordDecl *RD); - /// Gets the pure virtual member call function. + /// Gets the pure virtual member call. virtual StringRef GetPureVirtualCallName() = 0; + virtual llvm::Constant *GetPureVirtualFunction(); - /// Gets the deleted virtual member call name. + /// Gets the deleted virtual member call. virtual StringRef GetDeletedVirtualCallName() = 0; + virtual llvm::Constant *GetDeletedVirtualFunction(); + + llvm::Constant *getSpecialVirtualFn(StringRef name); /**************************** Array cookies ******************************/ diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp --- a/clang/lib/CodeGen/CGCXXABI.cpp +++ b/clang/lib/CodeGen/CGCXXABI.cpp @@ -180,6 +180,29 @@ return Address::invalid(); } +llvm::Constant *CGCXXABI::getSpecialVirtualFn(StringRef name) { + // For NVPTX devices in OpenMP emit special functon as null pointers, + // otherwise linking ends up with unresolved references. + if (CGM.getLangOpts().OpenMP && CGM.getLangOpts().OpenMPIsDevice && + CGM.getTriple().isNVPTX()) + return llvm::ConstantPointerNull::get(CGM.Int8PtrTy); + llvm::FunctionType *fnTy = + llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); + llvm::Constant *fn = + cast(CGM.CreateRuntimeFunction(fnTy, name).getCallee()); + if (auto f = dyn_cast(fn)) + f->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + return llvm::ConstantExpr::getBitCast(fn, CGM.Int8PtrTy); +} + +llvm::Constant *CGCXXABI::GetPureVirtualFunction() { + return getSpecialVirtualFn(GetPureVirtualCallName()); +} + +llvm::Constant *CGCXXABI::GetDeletedVirtualFunction() { + return getSpecialVirtualFn(GetDeletedVirtualCallName()); +} + bool CGCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, QualType elementType) { // If the class's usual deallocation function takes two arguments, diff --git a/clang/lib/CodeGen/CGVTables.h b/clang/lib/CodeGen/CGVTables.h --- a/clang/lib/CodeGen/CGVTables.h +++ b/clang/lib/CodeGen/CGVTables.h @@ -66,6 +66,19 @@ llvm::Constant *rtti, unsigned &nextVTableThunkIndex); + /// Get the constant to be added to a VTable. + llvm::Constant *getVTableComponent(const VTableLayout &layout, + unsigned componentIdx, + llvm::Constant *rtti, + unsigned &nextVTableThunkIndex); + + /// Make a function pointer into a relative integer when using the relative + /// vtables ABI. + llvm::Constant *makeComponentRelative(llvm::Constant *C, + llvm::GlobalVariable *VTable, + unsigned vtableIdx, + unsigned lastAddrPoint) const; + public: /// Add vtable components for the given vtable layout to the given /// global initializer. @@ -73,6 +86,11 @@ const VTableLayout &layout, llvm::Constant *rtti); + void createRelativeVTableInitializer(ConstantStructBuilder &builder, + llvm::GlobalVariable *VTable, + const VTableLayout &layout, + llvm::Constant *rtti); + CodeGenVTables(CodeGenModule &CGM); ItaniumVTableContext &getItaniumVTableContext() { diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -616,15 +616,123 @@ maybeEmitThunk(GD, Thunk, /*ForVTable=*/false); } +llvm::Constant *CodeGenVTables::makeComponentRelative( + llvm::Constant *C, llvm::GlobalVariable *VTable, unsigned vtableIdx, + unsigned lastAddrPoint) const { + // No need to get the offset of a nullptr. + if (C->isNullValue()) + return llvm::ConstantInt::get(CGM.Int32Ty, 0); + + llvm::Type *Int32Ty = CGM.Int32Ty; + llvm::Type *PtrDiffTy = + CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType()); + llvm::Type *VTableTy = VTable->getValueType(); + + llvm::Constant *gep = llvm::ConstantExpr::getGetElementPtr( + VTableTy, VTable, + llvm::ArrayRef{ + llvm::ConstantInt::get(Int32Ty, 0), + llvm::ConstantInt::get(Int32Ty, vtableIdx), + llvm::ConstantInt::get(Int32Ty, lastAddrPoint)}); + + llvm::Constant *AddrPointInt = + llvm::ConstantExpr::getPtrToInt(gep, PtrDiffTy); + llvm::Constant *Target; + + auto *GlobalVal = cast(C->stripPointerCastsAndAliases()); + llvm::Module &M = CGM.getModule(); + llvm::SmallString<16> Name(GlobalVal->getName()); + + // We don't want to copy the linkage of the vtable exactly because we still + // want the stub/proxy to be emitted for properlly caluclating the offset. + // Examples where there would bo no symbol emitted are available_externally + // and private linkages. + auto StubLinkage = VTable->hasLocalLinkage() + ? llvm::GlobalValue::InternalLinkage + : llvm::GlobalValue::ExternalLinkage; + + if (auto *Func = dyn_cast(GlobalVal)) { + llvm::SmallString<16> StubName(Name); + StubName.append(".stub"); + + // Take offset from stub to the vtable component. + llvm::Function *HiddenFunc = M.getFunction(StubName); + if (!HiddenFunc) { + // Make the stub private. + llvm::Function *Stub = llvm::Function::Create(Func->getFunctionType(), + StubLinkage, StubName, M); + + // Propogate byval attributes. + Stub->setAttributes(Func->getAttributes()); + + Stub->setDSOLocal(true); + Stub->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + if (!llvm::GlobalValue::isLocalLinkage(StubLinkage)) { + Stub->setVisibility(llvm::GlobalValue::HiddenVisibility); + Stub->setComdat(M.getOrInsertComdat(StubName)); + } + + // Fill the stub with a tail call that will be optimized. + llvm::BasicBlock *BB = + llvm::BasicBlock::Create(M.getContext(), "entry", Stub); + llvm::IRBuilder<> Builder(BB); + llvm::SmallVector Args; + for (auto &Arg : Stub->args()) + Args.push_back(&Arg); + llvm::CallInst *Call = Builder.CreateCall(Func, Args); + Call->setAttributes(Func->getAttributes()); + Call->setTailCall(); + if (Call->getType()->isVoidTy()) + Builder.CreateRetVoid(); + else + Builder.CreateRet(Call); + + HiddenFunc = Stub; + } + Target = HiddenFunc; + } else { + llvm::SmallString<16> RTTIProxyName(Name); + RTTIProxyName.append(".rtti_proxy"); + + // Component is RTTI. Create a private variable that points to this RTTI + // struct and take the offset of that. + llvm::GlobalVariable *GV = M.getNamedGlobal(RTTIProxyName); + if (!GV) { + GV = new llvm::GlobalVariable(CGM.getModule(), GlobalVal->getType(), + /*isConstant=*/true, StubLinkage, GlobalVal, + RTTIProxyName); + GV->setDSOLocal(true); + GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + if (!llvm::GlobalValue::isLocalLinkage(StubLinkage)) { + GV->setVisibility(llvm::GlobalValue::HiddenVisibility); + GV->setComdat(M.getOrInsertComdat(RTTIProxyName)); + } + } + Target = GV; + } + + llvm::Constant *ptrToInt = llvm::ConstantExpr::getPtrToInt(Target, PtrDiffTy); + return llvm::ConstantExpr::getIntegerCast( + llvm::ConstantExpr::getSub(ptrToInt, AddrPointInt), Int32Ty, + /*isSigned=*/true); +} + void CodeGenVTables::addVTableComponent( ConstantArrayBuilder &builder, const VTableLayout &layout, unsigned idx, llvm::Constant *rtti, unsigned &nextVTableThunkIndex) { - auto &component = layout.vtable_components()[idx]; + builder.add(getVTableComponent(layout, idx, rtti, nextVTableThunkIndex)); +} + +llvm::Constant * +CodeGenVTables::getVTableComponent(const VTableLayout &layout, + unsigned componentIdx, llvm::Constant *rtti, + unsigned &nextVTableThunkIndex) { + auto &component = layout.vtable_components()[componentIdx]; auto addOffsetConstant = [&](CharUnits offset) { - builder.add(llvm::ConstantExpr::getIntToPtr( + return llvm::ConstantExpr::getIntToPtr( llvm::ConstantInt::get(CGM.PtrDiffTy, offset.getQuantity()), - CGM.Int8PtrTy)); + CGM.Int8PtrTy); }; switch (component.getKind()) { @@ -638,7 +746,7 @@ return addOffsetConstant(component.getOffsetToTop()); case VTableComponent::CK_RTTI: - return builder.add(llvm::ConstantExpr::getBitCast(rtti, CGM.Int8PtrTy)); + return llvm::ConstantExpr::getBitCast(rtti, CGM.Int8PtrTy); case VTableComponent::CK_FunctionPointer: case VTableComponent::CK_CompleteDtorPointer: @@ -672,44 +780,28 @@ ? MD->hasAttr() : (MD->hasAttr() || !MD->hasAttr()); if (!CanEmitMethod) - return builder.addNullPointer(CGM.Int8PtrTy); + return llvm::ConstantExpr::getNullValue(CGM.Int8PtrTy); // Method is acceptable, continue processing as usual. } - auto getSpecialVirtualFn = [&](StringRef name) -> llvm::Constant * { - // For NVPTX devices in OpenMP emit special functon as null pointers, - // otherwise linking ends up with unresolved references. - if (CGM.getLangOpts().OpenMP && CGM.getLangOpts().OpenMPIsDevice && - CGM.getTriple().isNVPTX()) - return llvm::ConstantPointerNull::get(CGM.Int8PtrTy); - llvm::FunctionType *fnTy = - llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); - llvm::Constant *fn = cast( - CGM.CreateRuntimeFunction(fnTy, name).getCallee()); - if (auto f = dyn_cast(fn)) - f->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - return llvm::ConstantExpr::getBitCast(fn, CGM.Int8PtrTy); - }; - llvm::Constant *fnPtr; // Pure virtual member functions. if (cast(GD.getDecl())->isPure()) { if (!PureVirtualFn) - PureVirtualFn = - getSpecialVirtualFn(CGM.getCXXABI().GetPureVirtualCallName()); + PureVirtualFn = CGM.getCXXABI().GetPureVirtualFunction(); fnPtr = PureVirtualFn; // Deleted virtual member functions. } else if (cast(GD.getDecl())->isDeleted()) { if (!DeletedVirtualFn) - DeletedVirtualFn = - getSpecialVirtualFn(CGM.getCXXABI().GetDeletedVirtualCallName()); + DeletedVirtualFn = CGM.getCXXABI().GetDeletedVirtualFunction(); fnPtr = DeletedVirtualFn; // Thunks. } else if (nextVTableThunkIndex < layout.vtable_thunks().size() && - layout.vtable_thunks()[nextVTableThunkIndex].first == idx) { + layout.vtable_thunks()[nextVTableThunkIndex].first == + componentIdx) { auto &thunkInfo = layout.vtable_thunks()[nextVTableThunkIndex].second; nextVTableThunkIndex++; @@ -721,25 +813,49 @@ fnPtr = CGM.GetAddrOfFunction(GD, fnTy, /*ForVTable=*/true); } - fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy); - builder.add(fnPtr); - return; + return llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy); } case VTableComponent::CK_UnusedFunctionPointer: - return builder.addNullPointer(CGM.Int8PtrTy); + return llvm::ConstantExpr::getNullValue(CGM.Int8PtrTy); } llvm_unreachable("Unexpected vtable component kind"); } llvm::Type *CodeGenVTables::getVTableType(const VTableLayout &layout) { - SmallVector tys; + SmallVector types; for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) { - tys.push_back(llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i))); + if (!CGM.getLangOpts().FuchsiaRelativeCXXABIVTables) { + types.push_back( + llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i))); + } else { + SmallVector innerTypes; + size_t thisIndex = layout.getVTableOffset(i); + size_t nextIndex = thisIndex + layout.getVTableSize(i); + for (unsigned componentIdx = thisIndex; componentIdx != nextIndex; + ++componentIdx) { + auto &component = layout.vtable_components()[componentIdx]; + if (component.isFunctionPointerKind()) { + innerTypes.push_back(CGM.Int32Ty); + } else if (component.isRTTIKind()) { + // NOTE: A bunch of RTTI depends on expecting that the offset to top, + // and RTTI struct are i8*s, or at least 64 bits (see __dynamic_cast + // in libcxx), so when we decide + // to tackle RTTI usage, we will either need to change a bunch of rtti + // functions, or keep this as an i8* (or an i64, or an i32 with + // padding) then add the PLT64 to lld for x86 since it currently + // doesn't seem to be handled yet. + innerTypes.push_back(CGM.Int32Ty); + } else { + innerTypes.push_back(CGM.Int8PtrTy); + } + } + types.push_back(llvm::StructType::get(CGM.getLLVMContext(), innerTypes)); + } } - return llvm::StructType::get(CGM.getLLVMContext(), tys); + return llvm::StructType::get(CGM.getLLVMContext(), types); } void CodeGenVTables::createVTableInitializer(ConstantStructBuilder &builder, @@ -750,19 +866,55 @@ auto vtableElem = builder.beginArray(CGM.Int8PtrTy); size_t thisIndex = layout.getVTableOffset(i); size_t nextIndex = thisIndex + layout.getVTableSize(i); - for (unsigned i = thisIndex; i != nextIndex; ++i) { + for (unsigned i = thisIndex; i != nextIndex; ++i) addVTableComponent(vtableElem, layout, i, rtti, nextVTableThunkIndex); + vtableElem.finishAndAddTo(builder); + } +} + +void CodeGenVTables::createRelativeVTableInitializer( + ConstantStructBuilder &builder, llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, llvm::Constant *RTTI) { + unsigned nextVTableThunkIndex = 0; + for (unsigned vtableIdx = 0; vtableIdx != VTLayout.getNumVTables(); + ++vtableIdx) { + auto vtableElem = builder.beginStruct(); + + unsigned lastAddrPoint = 0; + size_t thisIndex = VTLayout.getVTableOffset(vtableIdx); + size_t nextIndex = thisIndex + VTLayout.getVTableSize(vtableIdx); + for (unsigned componentIdx = thisIndex; componentIdx != nextIndex; + ++componentIdx) { + // We want to point to the first function in the vtable. This will come + // after the RTTI component, so we can just check if the previous + // component was RTTI. + // FIXME: Need a better way of identifying address points. + if (componentIdx > 0 && + VTLayout.vtable_components()[componentIdx - 1].isRTTIKind()) + lastAddrPoint = componentIdx; + + llvm::Constant *component = getVTableComponent( + VTLayout, componentIdx, RTTI, nextVTableThunkIndex); + + auto &layoutComponent = VTLayout.vtable_components()[componentIdx]; + if (layoutComponent.isFunctionPointerKind()) { + component = makeComponentRelative(component, VTable, vtableIdx, + lastAddrPoint - thisIndex); + } else if (layoutComponent.isRTTIKind()) { + component = makeComponentRelative(component, VTable, vtableIdx, 2); + } + + vtableElem.add(component); } + vtableElem.finishAndAddTo(builder); } } -llvm::GlobalVariable * -CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD, - const BaseSubobject &Base, - bool BaseIsVirtual, - llvm::GlobalVariable::LinkageTypes Linkage, - VTableAddressPointsMapTy& AddressPoints) { +llvm::GlobalVariable *CodeGenVTables::GenerateConstructionVTable( + const CXXRecordDecl *RD, const BaseSubobject &Base, bool BaseIsVirtual, + llvm::GlobalVariable::LinkageTypes Linkage, + VTableAddressPointsMapTy &AddressPoints) { if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) DI->completeClassData(Base.getBase()); @@ -804,10 +956,7 @@ CGM.getContext().getTagDeclType(Base.getBase())); // Create and set the initializer. - ConstantInitBuilder builder(CGM); - auto components = builder.beginStruct(); - createVTableInitializer(components, *VTLayout, RTTI); - components.finishAndSetAsInitializer(VTable); + CGM.getCXXABI().SetVTableInitializer(*this, VTable, *VTLayout, RTTI); // Set properties only after the initializer has been set to ensure that the // GV is treated as definition and not declaration. diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -235,6 +235,16 @@ void emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) override; + virtual void SetVTableInitializer(CodeGenVTables &CGVT, + llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI) override { + ConstantInitBuilder builder(CGM); + auto components = builder.beginStruct(); + CGVT.createVTableInitializer(components, VTLayout, RTTI); + components.finishAndSetAsInitializer(VTable); + } + bool isVirtualOffsetNeededForVTableField(CodeGenFunction &CGF, CodeGenFunction::VPtr Vptr) override; @@ -261,6 +271,8 @@ llvm::GlobalVariable *getAddrOfVTable(const CXXRecordDecl *RD, CharUnits VPtrOffset) override; + virtual llvm::Constant *getVTableAddressPointForTypeInfo(llvm::Constant *VTable) const; + CGCallee getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, Address This, llvm::Type *Ty, SourceLocation Loc) override; @@ -376,6 +388,30 @@ /// across a program. virtual bool shouldRTTIBeUnique() const { return true; } + virtual llvm::Value *GetVirtualFunctionFromVTable(CodeGenFunction &CGF, + const CXXRecordDecl *RD, + llvm::Value *VTable, + uint64_t VTableIndex, + llvm::Type *Ty); + + virtual llvm::Value * + GetPointerToMemberVirtualFunction(CodeGenFunction &CGF, llvm::Value *VTable, + llvm::Value *VTableOffset, + llvm::FunctionType *FTy) { + llvm::Value *VFPAddr = CGF.Builder.CreateGEP(VTable, VTableOffset); + VFPAddr = + CGF.Builder.CreateBitCast(VFPAddr, FTy->getPointerTo()->getPointerTo()); + return CGF.Builder.CreateAlignedLoad(VFPAddr, CGF.getPointerAlign(), + "memptr.virtualfn"); + } + + virtual uint64_t getVTableOffset(uint64_t Index) { + const ASTContext &Context = getContext(); + CharUnits PointerWidth = + Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); + return Index * PointerWidth.getQuantity(); + } + public: /// What sort of unique-RTTI behavior should we use? enum RTTIUniquenessKind { @@ -493,6 +529,91 @@ explicit FuchsiaCXXABI(CodeGen::CodeGenModule &CGM) : ItaniumCXXABI(CGM) {} + void SetVTableInitializer(CodeGenVTables &CGVT, llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI) override { + ConstantInitBuilder builder(CGM); + auto components = builder.beginStruct(); + CGVT.createRelativeVTableInitializer(components, VTable, VTLayout, RTTI); + components.finishAndSetAsInitializer(VTable); + } + + llvm::Constant *GetPureVirtualFunction() override { + // FIXME: There is currently an issue (PR43094) where + // when merging comdat groups, lld can select a local symbol as the + // signature symbol even though it cannot be accessed outside that + // symbol's module. A result of this ABI would make __cxa_pure_virtual + // (and it's destructor equivalent) local symbols, and depending on + // link order, the comdat groups would resolve to the one with the + // local symbol. As a temporary solution, since we shouldn't be + // calling these functions in the first place, fill these components + // with zero. + return llvm::Constant::getNullValue(CGM.Int8PtrTy); + } + + llvm::Constant *GetDeletedVirtualFunction() override { + return llvm::Constant::getNullValue(CGM.Int8PtrTy); + } + + llvm::Value *EmitTypeid(CodeGenFunction &CGF, + QualType SrcRecordTy, + Address ThisPtr, + llvm::Type *StdTypeInfoPtrTy) override { + auto *ClassDecl = + cast(SrcRecordTy->castAs()->getDecl()); + llvm::Value *Value = + CGF.GetVTablePtr(ThisPtr, StdTypeInfoPtrTy->getPointerTo(), ClassDecl); + + // Load the type info. + //Value = CGF.Builder.CreateConstInBoundsGEP1_64(Value, -1ULL); + //return CGF.Builder.CreateAlignedLoad(Value, CGF.getPointerAlign()); + Value = CGF.Builder.CreateBitCast(Value, CGM.Int8PtrTy); + Value = CGF.Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::load_relative, {CGM.Int32Ty}), + {Value, llvm::ConstantInt::get(CGM.Int32Ty, -4)}); + + // Dereference again since this is a proxy we accessed. + Value = CGF.Builder.CreateBitCast(Value, StdTypeInfoPtrTy->getPointerTo()); + return CGF.Builder.CreateAlignedLoad(Value, CGF.getPointerAlign()); + } + + llvm::Constant *getVTableAddressPointForTypeInfo(llvm::Constant *VTable) const override { + // The vtable address point is 12 bytes after its start: + // 8 for the offset to top + 4 for the relative offset to rtti + llvm::Constant *Twelve = llvm::ConstantInt::get(CGM.Int32Ty, 12); + VTable = llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy); + return llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.Int8Ty, VTable, Twelve); + } + +protected: + llvm::Value *GetVirtualFunctionFromVTable(CodeGenFunction &CGF, + const CXXRecordDecl *RD, + llvm::Value *VTable, + uint64_t VTableIndex, + llvm::Type *Ty) override { + VTable = CGF.Builder.CreateBitCast(VTable, CGM.Int8PtrTy); + llvm::Value *Load = CGF.Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::load_relative, {CGM.Int32Ty}), + {VTable, llvm::ConstantInt::get(CGM.Int32Ty, 4 * VTableIndex)}); + return CGF.Builder.CreateBitCast(Load, Ty->getPointerTo()); + } + + llvm::Value * + GetPointerToMemberVirtualFunction(CodeGenFunction &CGF, llvm::Value *VTable, + llvm::Value *VTableOffset, + llvm::FunctionType *FTy) override { + llvm::Value *VirtualFn = + CGF.Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::load_relative, + {VTableOffset->getType()}), + {VTable, VTableOffset}); + return CGF.Builder.CreateBitCast(VirtualFn, FTy->getPointerTo()); + } + + uint64_t getVTableOffset(uint64_t Index) override { + // Multiply by 4-byte relative offsets. + return Index * 4; + } + private: bool HasThisReturn(GlobalDecl GD) const override { return isa(GD.getDecl()) || @@ -702,9 +823,9 @@ TypeId = llvm::MetadataAsValue::get(CGF.getLLVMContext(), MD); } - llvm::Value *VFPAddr = Builder.CreateGEP(VTable, VTableOffset); - if (ShouldEmitVFEInfo) { + llvm::Value *VFPAddr = Builder.CreateGEP(VTable, VTableOffset); + // If doing VFE, load from the vtable with a type.checked.load intrinsic // call. Note that we use the GEP to calculate the address to load from // and pass 0 as the offset to the intrinsic. This is because every @@ -721,14 +842,14 @@ // When not doing VFE, emit a normal load, as it allows more // optimisations than type.checked.load. if (ShouldEmitCFICheck || ShouldEmitWPDInfo) { + llvm::Value *VFPAddr = Builder.CreateGEP(VTable, VTableOffset); CheckResult = Builder.CreateCall( CGM.getIntrinsic(llvm::Intrinsic::type_test), {Builder.CreateBitCast(VFPAddr, CGF.Int8PtrTy), TypeId}); } - VFPAddr = - Builder.CreateBitCast(VFPAddr, FTy->getPointerTo()->getPointerTo()); - VirtualFn = Builder.CreateAlignedLoad(VFPAddr, CGF.getPointerAlign(), - "memptr.virtualfn"); + + VirtualFn = + GetPointerToMemberVirtualFunction(CGF, VTable, VTableOffset, FTy); } assert(VirtualFn && "Virtual fuction pointer not created!"); assert((!ShouldEmitCFICheck || !ShouldEmitVFEInfo || !ShouldEmitWPDInfo || @@ -1003,11 +1124,7 @@ llvm::Constant *MemPtr[2]; if (MD->isVirtual()) { uint64_t Index = CGM.getItaniumVTableContext().getMethodVTableIndex(MD); - - const ASTContext &Context = getContext(); - CharUnits PointerWidth = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); - uint64_t VTableOffset = (Index * PointerWidth.getQuantity()); + uint64_t VTableOffset = getVTableOffset(Index); if (UseARMMethodPtrABI) { // ARM C++ ABI 3.2.1: @@ -1678,15 +1795,12 @@ llvm::Constant *RTTI = CGM.GetAddrOfRTTIDescriptor(CGM.getContext().getTagDeclType(RD)); - // Create and set the initializer. - ConstantInitBuilder Builder(CGM); - auto Components = Builder.beginStruct(); - CGVT.createVTableInitializer(Components, VTLayout, RTTI); - Components.finishAndSetAsInitializer(VTable); - // Set the correct linkage. VTable->setLinkage(Linkage); + // Create and set the initializer. + SetVTableInitializer(CGVT, VTable, VTLayout, RTTI); + if (CGM.supportsCOMDAT() && VTable->isWeakForLinker()) VTable->setComdat(CGM.getModule().getOrInsertComdat(VTable->getName())); @@ -1807,14 +1921,35 @@ return VTable; } +llvm::Constant *ItaniumCXXABI::getVTableAddressPointForTypeInfo(llvm::Constant *VTable) const { + llvm::Type *PtrDiffTy = + CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType()); + + // The vtable address point is 2. + llvm::Constant *Two = llvm::ConstantInt::get(PtrDiffTy, 2); + VTable = + llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.Int8PtrTy, VTable, Two); + return llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy); +} + +llvm::Value *ItaniumCXXABI::GetVirtualFunctionFromVTable( + CodeGenFunction &CGF, const CXXRecordDecl *RD, llvm::Value *VTable, + uint64_t VTableIndex, llvm::Type *Ty) { + VTable = + CGF.Builder.CreateBitCast(VTable, Ty->getPointerTo()->getPointerTo()); + llvm::Value *VTableSlotPtr = + CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); + return CGF.Builder.CreateAlignedLoad(VTableSlotPtr, CGF.getPointerAlign()); +} + CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, Address This, llvm::Type *Ty, SourceLocation Loc) { - Ty = Ty->getPointerTo()->getPointerTo(); auto *MethodDecl = cast(GD.getDecl()); - llvm::Value *VTable = CGF.GetVTablePtr(This, Ty, MethodDecl->getParent()); + llvm::Value *VTable = CGF.GetVTablePtr( + This, Ty->getPointerTo()->getPointerTo(), MethodDecl->getParent()); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); llvm::Value *VFunc; @@ -1825,10 +1960,8 @@ } else { CGF.EmitTypeMetadataCodeForVCall(MethodDecl->getParent(), VTable, Loc); - llvm::Value *VFuncPtr = - CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); - auto *VFuncLoad = - CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); + auto *VFuncLoad = GetVirtualFunctionFromVTable(CGF, MethodDecl->getParent(), + VTable, VTableIndex, Ty); // Add !invariant.load md to virtual function load to indicate that // function didn't change inside vtable. @@ -1837,11 +1970,14 @@ // the same virtual function loads from the same vtable load, which won't // happen without enabled devirtualization with -fstrict-vtable-pointers. if (CGM.getCodeGenOpts().OptimizationLevel > 0 && - CGM.getCodeGenOpts().StrictVTablePointers) - VFuncLoad->setMetadata( - llvm::LLVMContext::MD_invariant_load, - llvm::MDNode::get(CGM.getLLVMContext(), - llvm::ArrayRef())); + CGM.getCodeGenOpts().StrictVTablePointers) { + if (auto *VFuncLoadInstr = dyn_cast(VFuncLoad)) { + VFuncLoadInstr->setMetadata( + llvm::LLVMContext::MD_invariant_load, + llvm::MDNode::get(CGM.getLLVMContext(), + llvm::ArrayRef())); + } + } VFunc = VFuncLoad; } @@ -3300,14 +3436,7 @@ CGM.getModule().getOrInsertGlobal(VTableName, CGM.Int8PtrTy); CGM.setDSOLocal(cast(VTable->stripPointerCasts())); - llvm::Type *PtrDiffTy = - CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType()); - - // The vtable address point is 2. - llvm::Constant *Two = llvm::ConstantInt::get(PtrDiffTy, 2); - VTable = - llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.Int8PtrTy, VTable, Two); - VTable = llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy); + VTable = CXXABI.getVTableAddressPointForTypeInfo(VTable); Fields.push_back(VTable); } diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -269,6 +269,10 @@ void emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) override; + void SetVTableInitializer(CodeGenVTables &CGVT, llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI) override; + bool isVirtualOffsetNeededForVTableField(CodeGenFunction &CGF, CodeGenFunction::VPtr Vptr) override; @@ -1672,6 +1676,16 @@ CGM.AddVTableTypeMetadata(VTable, AddressPoint, RD); } +void MicrosoftCXXABI::SetVTableInitializer(CodeGenVTables &CGVT, + llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI) { + ConstantInitBuilder builder(CGM); + auto components = builder.beginStruct(); + CGVT.createVTableInitializer(components, VTLayout, RTTI); + components.finishAndSetAsInitializer(VTable); +} + void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) { MicrosoftVTableContext &VFTContext = CGM.getMicrosoftVTableContext(); @@ -1690,10 +1704,7 @@ [](const VTableComponent &VTC) { return VTC.isRTTIKind(); })) RTTI = getMSCompleteObjectLocator(RD, *Info); - ConstantInitBuilder Builder(CGM); - auto Components = Builder.beginStruct(); - CGVT.createVTableInitializer(Components, VTLayout, RTTI); - Components.finishAndSetAsInitializer(VTable); + SetVTableInitializer(CGVT, VTable, VTLayout, RTTI); emitVTableTypeMetadata(*Info, RD, VTable); } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6008,6 +6008,13 @@ CmdArgs.push_back("-fwhole-program-vtables"); } + // Add unstable C++ ABI flags. + if (Args.hasArg(options::OPT_fexperimental_fuchsia_relative_cxx_abi_vtables)) + CmdArgs.push_back("-fexperimental-fuchsia-relative-c++-abi-vtables"); + if (Args.hasArg( + options::OPT_fno_experimental_fuchsia_relative_cxx_abi_vtables)) + CmdArgs.push_back("-fno-experimental-fuchsia-relative-c++-abi-vtables"); + bool DefaultsSplitLTOUnit = (WholeProgramVTables || Sanitize.needsLTO()) && (D.getLTOMode() == LTOK_Full || TC.canSplitThinLTOUnit()); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3345,6 +3345,11 @@ Opts.BuildingPCHWithObjectFile = Args.hasArg(OPT_building_pch_with_obj); Opts.MaxTokens = getLastArgIntValue(Args, OPT_fmax_tokens_EQ, 0, Diags); + llvm::Triple Triple = llvm::Triple(TargetOpts.Triple); + Opts.FuchsiaRelativeCXXABIVTables = + (Args.hasArg(OPT_fexperimental_fuchsia_relative_cxx_abi_vtables) || + Triple.isOSFuchsia()) && + !Args.hasArg(OPT_fno_experimental_fuchsia_relative_cxx_abi_vtables); } static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { diff --git a/clang/test/CodeGenCXX/Fuchsia/child-inheritted-from-parent-in-comdat.cpp b/clang/test/CodeGenCXX/Fuchsia/child-inheritted-from-parent-in-comdat.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/child-inheritted-from-parent-in-comdat.cpp @@ -0,0 +1,53 @@ +// Cross comdat example +// Parent VTable is in a comdat section. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: $_ZN1B3fooEv.stub = comdat any + +// The inline function is emitted in each module with the same comdat +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZTS1A = comdat any +// CHECK: $_ZTI1A = comdat any +// CHECK: $_ZTI1B.rtti_proxy = comdat any + +// The VTable is emitted everywhere used +// CHECK: $_ZTV1A = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any + +// The VTable for B is emitted here since it has a key function which is defined in this module +// CHECK: @_ZTV1B = dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// The VTable for A is emitted here and in a comdat section since it has no key function, and is used in this module when creating an instance of A (in func()). +// CHECK: @_ZTV1A = linkonce_odr dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat, align 8 + +// CHECK: define dso_local void @_ZN1B3fooEv(%class.B* nocapture %this) unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define hidden void @_ZN1B3fooEv.stub(%class.B* nocapture %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define hidden void @_ZN1A3fooEv.stub(%class.A* nocapture readnone %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + inline virtual void foo() {} +}; +class B : public A { +public: + void foo() override; +}; +void A_foo(A *a); + +void B::foo() {} +void func2() { + A a; + A_foo(&a); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/child-vtable-in-comdat.cpp b/clang/test/CodeGenCXX/Fuchsia/child-vtable-in-comdat.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/child-vtable-in-comdat.cpp @@ -0,0 +1,54 @@ +// Cross comdat example +// Child VTable is in a comdat section. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: $_ZN1B3fooEv.stub = comdat any +// CHECK: $_ZN1A3fooEv.stub = comdat any + +// A comdat is emitted for B but not A +// CHECK: $_ZTV1B = comdat any +// CHECK: $_ZTS1B = comdat any +// CHECK: $_ZTI1B = comdat any +// CHECK: $_ZTI1B.rtti_proxy = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any + +// VTable for B is emitted here since we access it when creating an instance of B. The VTable is also linkonce_odr and in its own comdat. +// CHECK: @_ZTV1B = linkonce_odr dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat, align 8 + +// The RTTI objects aren’t that important, but it is good to know that they are emitted here since they are used in the vtable for B, and external references are used for RTTI stuff from A. +// CHECK: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global i8* +// CHECK: @_ZTS1B = linkonce_odr dso_local constant [3 x i8] c"1B\00", comdat, align 1 +// CHECK: @_ZTI1A = external constant i8* +// CHECK: @_ZTI1B = linkonce_odr dso_local constant { i8*, i8*, i8* } { i8* getelementptr inbounds (i8, i8* bitcast (i8** @_ZTVN10__cxxabiv120__si_class_type_infoE to i8*), i32 12), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1B, i32 0, i32 0), i8* bitcast (i8** @_ZTI1A to i8*) }, comdat, align 8 +// CHECK: @_ZTI1B.rtti_proxy = hidden unnamed_addr constant { i8*, i8*, i8* }* @_ZTI1B, comdat +// CHECK: @_ZTI1A.rtti_proxy = hidden unnamed_addr constant i8** @_ZTI1A, comdat + +// CHECK: define hidden void @_ZN1B3fooEv.stub(%class.B* nocapture readnone %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: declare void @_ZN1A3fooEv(%class.A*) unnamed_addr + +// CHECK: define hidden void @_ZN1A3fooEv.stub(%class.A* %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @_ZN1A3fooEv(%class.A* %0) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); +}; +class B : public A { +public: + inline void foo() override {} +}; +void A_foo(A *a); + +// func() is used so that the vtable for B is accessed when creating the instance. +void func() { + B b; + A_foo(&b); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/cross-translation-unit-1.cpp b/clang/test/CodeGenCXX/Fuchsia/cross-translation-unit-1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/cross-translation-unit-1.cpp @@ -0,0 +1,38 @@ +// Check the vtable layout for classes with key functions defined in different +// translation units. This TU only manifests the vtable for A. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +#include "cross-tu-header.h" + +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZN1A3barEv.stub = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any + +// CHECK: @_ZTV1A = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// A::foo() is still available for other modules to use since it is not marked with private or internal linkage. +// CHECK: define dso_local void @_ZN1A3fooEv(%class.A* nocapture %this) unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// The proxy that we take a reference to in the vtable has hidden visibility and external linkage so it can be used only by other modules in the same DSO. A::foo() is inlined into this stub since it is defined in the same module. +// CHECK: define hidden void @_ZN1A3fooEv.stub(%class.A* nocapture %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// A::bar() is called within the module but not defined, even though the VTable for A is emitted here +// CHECK: declare void @_ZN1A3barEv(%class.A*) unnamed_addr + +// The stub for A::bar() is made private, so it will not appear in the symbol table and is only used in this module. We tail call here because A::bar() is not defined in the same module. +// CHECK: define hidden void @_ZN1A3barEv.stub(%class.A* %0) unnamed_addr {{#[0-9]+}} comdat { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @_ZN1A3barEv(%class.A* %0) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void A::foo() {} +void A_foo(A *a) { a->foo(); } +void A_bar(A *a) { a->bar(); } diff --git a/clang/test/CodeGenCXX/Fuchsia/cross-translation-unit-2.cpp b/clang/test/CodeGenCXX/Fuchsia/cross-translation-unit-2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/cross-translation-unit-2.cpp @@ -0,0 +1,37 @@ +// Check the vtable layout for classes with key functions defined in different +// translation units. This TU manifests the vtable for B. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +#include "cross-tu-header.h" + +// CHECK: $_ZN1B3fooEv.stub = comdat any +// CHECK: $_ZN1A3barEv.stub = comdat any +// CHECK: $_ZTI1B.rtti_proxy = comdat any + +// CHECK: @_ZTV1B = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// A::bar() is defined outside of the module that defines the vtable for A +// CHECK: define dso_local void @_ZN1A3barEv(%class.A* nocapture %this) unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define dso_local void @_ZN1B3fooEv(%class.B* nocapture %this) unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// The stubs for B::foo() and A::bar() are hidden +// CHECK: define hidden void @_ZN1B3fooEv.stub(%class.B* nocapture %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define hidden void @_ZN1A3barEv.stub(%class.A* nocapture %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void A::bar() {} +void B::foo() {} diff --git a/clang/test/CodeGenCXX/Fuchsia/cross-tu-header.h b/clang/test/CodeGenCXX/Fuchsia/cross-tu-header.h new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/cross-tu-header.h @@ -0,0 +1,10 @@ +class A { +public: + virtual void foo(); + virtual void bar(); +}; + +class B : public A { +public: + void foo() override; +}; diff --git a/clang/test/CodeGenCXX/Fuchsia/diamond-inheritance.cpp b/clang/test/CodeGenCXX/Fuchsia/diamond-inheritance.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/diamond-inheritance.cpp @@ -0,0 +1,53 @@ +// Diamond inheritance. +// A more complicated multiple inheritance example that includes longer chain of inheritance and a common ancestor. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: %class.B = type { %class.A } +// CHECK: %class.A = type { i32 (...)** } +// CHECK: %class.C = type { %class.A } +// CHECK: %class.D = type { %class.B, %class.C } + +// VTable for B should contain offset to top (0), RTTI pointer, A::foo(), and B::barB(). +// CHECK: @_ZTV1B = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B4barBEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// VTable for C should contain offset to top (0), RTTI pointer, A::foo(), and C::barC(). +// CHECK: @_ZTV1C = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZN1C4barCEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// VTable for D should be similar to the mutiple inheritance example where this +// vtable contains 2 inner vtables: +// - 1st table containing D::foo(), B::barB(), and D::baz(). +// - 2nd table containing a thunk to D::foo() and C::barC(). +// CHECK: @_ZTV1D = dso_local unnamed_addr constant { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }* @_ZTV1D, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* @_ZN1D3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }* @_ZTV1D, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B4barBEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }* @_ZTV1D, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* @_ZN1D3bazEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }* @_ZTV1D, i32 0, i32 0, i32 2) to i64)) to i32) }, { i8*, i32, i32, i32 } { i8* inttoptr (i64 -8 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }* @_ZTV1D, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* @_ZThn8_N1D3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }* @_ZTV1D, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZN1C4barCEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32, i32 }, { i8*, i32, i32, i32 } }* @_ZTV1D, i32 0, i32 1, i32 2) to i64)) to i32) } }, align 8 + +class A { +public: + virtual void foo(); +}; + +class B : public A { +public: + virtual void barB(); +}; + +class C : public A { + virtual void barC(); +}; + +// Should be a struct with 2 arrays from 2 parents. +// The 1st contains D::foo(), B::barB(), and D::baz(). +// The 2nd contains C::barC(), and a thunk that points to D::foo(). +class D : public B, C { +public: + virtual void baz(); + void foo() override; +}; + +void B::barB() {} +void C::barC() {} +void D::foo() {} +void D::baz() {} + +void D_foo(D *d) { + d->foo(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/diamond-virtual-inheritance.cpp b/clang/test/CodeGenCXX/Fuchsia/diamond-virtual-inheritance.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/diamond-virtual-inheritance.cpp @@ -0,0 +1,87 @@ +// Diamond virtual inheritance. +// This should cover virtual inheritance, construction vtables, and VTTs. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: %class.B = type { i32 (...)**, %class.A.base } +// CHECK: %class.A.base = type <{ i32 (...)**, i32 }> +// CHECK: %class.C = type { i32 (...)**, %class.A.base } +// CHECK: %class.D = type { %class.B.base, %class.C.base, %class.A.base } +// CHECK: %class.B.base = type { i32 (...)** } +// CHECK: %class.C.base = type { i32 (...)** } + +// Class A contains a vtable ptr, then int, then padding +// CHECK: %class.A = type <{ i32 (...)**, i32, [4 x i8] }> + +// VTable for B. Contains an extra field at the start for the virtual-base offset. +// CHECK: @_ZTV1B = dso_local unnamed_addr constant { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } } { { i8*, i8*, i32, i32 } { i8* inttoptr (i64 8 to i8*), i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B4barBEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 3) to i64)) to i32) }, { i8*, i8*, i32, i32 } { i8* null, i8* inttoptr (i64 -8 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 1, i32 3) to i64)) to i32) } }, align 8 + +// VTT for B +// CHECK: @_ZTT1B = dso_local unnamed_addr constant [2 x i8*] [i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1B, i32 0, inrange i32 0, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1B, i32 0, inrange i32 1, i32 3) to i8*)], align 8 + +// VTable for C +// CHECK: @_ZTV1C = dso_local unnamed_addr constant { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } } { { i8*, i8*, i32, i32 } { i8* inttoptr (i64 8 to i8*), i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZN1C4barCEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 3) to i64)) to i32) }, { i8*, i8*, i32, i32 } { i8* null, i8* inttoptr (i64 -8 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 1, i32 3) to i64)) to i32) } }, align 8 + +// VTT for C +// CHECK: @_ZTT1C = dso_local unnamed_addr constant [2 x i8*] [i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1C, i32 0, inrange i32 0, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1C, i32 0, inrange i32 1, i32 3) to i8*)], align 8 + +// VTable for D +// CHECK: @_ZTV1D = dso_local unnamed_addr constant { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } } { { i8*, i8*, i32, i32, i32 } { i8* inttoptr (i64 16 to i8*), i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B4barBEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, i32 0, i32 3) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* @_ZN1D3bazEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, i32 0, i32 3) to i64)) to i32) }, { i8*, i8*, i32, i32 } { i8* inttoptr (i64 8 to i8*), i8* inttoptr (i64 -8 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZN1C4barCEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, i32 1, i32 3) to i64)) to i32) }, { i8*, i8*, i32, i32 } { i8* null, i8* inttoptr (i64 -16 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, i32 2, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, i32 2, i32 3) to i64)) to i32) } }, align 8 + +// VTT for D +// CHECK: @_ZTT1D = dso_local unnamed_addr constant [7 x i8*] [i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, inrange i32 0, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D0_1B, i32 0, inrange i32 0, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D0_1B, i32 0, inrange i32 1, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D8_1C, i32 0, inrange i32 0, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D8_1C, i32 0, inrange i32 1, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, inrange i32 2, i32 3) to i8*), i8* bitcast (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32, i32 }, { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTV1D, i32 0, inrange i32 1, i32 3) to i8*)], align 8 + +// Construction vtable for B-in-D +// CHECK: @_ZTC1D0_1B = dso_local unnamed_addr constant { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } } { { i8*, i8*, i32, i32 } { i8* inttoptr (i64 16 to i8*), i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D0_1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B4barBEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D0_1B, i32 0, i32 0, i32 3) to i64)) to i32) }, { i8*, i8*, i32, i32 } { i8* null, i8* inttoptr (i64 -16 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D0_1B, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D0_1B, i32 0, i32 1, i32 3) to i64)) to i32) } }, align 8 + +// Construction vtable for C-in-D +// CHECK: @_ZTC1D8_1C = dso_local unnamed_addr constant { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } } { { i8*, i8*, i32, i32 } { i8* inttoptr (i64 8 to i8*), i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D8_1C, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZN1C4barCEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D8_1C, i32 0, i32 0, i32 3) to i64)) to i32) }, { i8*, i8*, i32, i32 } { i8* null, i8* inttoptr (i64 -8 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D8_1C, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }, { { i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 } }* @_ZTC1D8_1C, i32 0, i32 1, i32 3) to i64)) to i32) } }, align 8 + +// CHECK: define dso_local void @_Z5D_fooP1D(%class.D* %d) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[d:%[0-9]+]] = bitcast %class.D* %d to i8** +// CHECK-NEXT: [[vtable:%[a-z0-9]+]] = load i8*, i8** [[d]], align 8 + +// This should be -20 since the virtual offset to top is 20 bytes from the address point (8 bytes for the virtual call, 8 bytes for the offset to top, and 4 bytes for the RTTI offset) +// CHECK-NEXT: [[vbase_offset_ptr:%[a-z0-9.]+]] = getelementptr i8, i8* [[vtable]], i64 -20 + +// CHECK-NEXT: [[vbase_offset_ptr2:%[a-z0-9.]+]] = bitcast i8* [[vbase_offset_ptr]] to i64* +// CHECK-NEXT: [[vbase_offset:%[a-z0-9.]+]] = load i64, i64* [[vbase_offset_ptr2]], align 8 +// CHECK-NEXT: [[d:%[0-9]+]] = bitcast %class.D* %d to i8* +// CHECK-NEXT: [[add_ptr:%[a-z0-9.]+]] = getelementptr inbounds i8, i8* [[d]], i64 [[vbase_offset]] +// CHECK-NEXT: [[a:%[0-9]+]] = bitcast i8* [[add_ptr]] to %class.A* +// CHECK-NEXT: [[a_i8_ptr:%[0-9]+]] = bitcast i8* [[add_ptr]] to i8** +// CHECK-NEXT: [[vtable:%[a-z0-9]+]] = load i8*, i8** [[a_i8_ptr]], align 8 +// CHECK-NEXT: [[ptr:%[0-9]+]] = call i8* @llvm.load.relative.i32(i8* [[vtable]], i32 0) +// CHECK-NEXT: [[method:%[0-9]+]] = bitcast i8* [[ptr]] to void (%class.A*)* +// CHECK-NEXT: call void [[method]](%class.A* [[a]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); + int a; +}; + +class B : public virtual A { +public: + virtual void barB(); +}; + +class C : public virtual A { + virtual void barC(); +}; + +class D : public B, C { +public: + virtual void baz(); +}; + +void B::barB() {} +void C::barC() {} +void D::baz() {} + +void D_foo(D *d) { + d->foo(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/dynamic-cast.cpp b/clang/test/CodeGenCXX/Fuchsia/dynamic-cast.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/dynamic-cast.cpp @@ -0,0 +1,77 @@ +// dynamic_cast +// Ensure that dynamic casting works normally + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O3 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: define dso_local %class.A* @_Z6upcastP1B(%class.B* readnone %b) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[a:%[0-9]+]] = getelementptr %class.B, %class.B* %b, i64 0, i32 0 +// CHECK-NEXT: ret %class.A* [[a]] +// CHECK-NEXT: } + +// CHECK: define dso_local %class.B* @_Z8downcastP1A(%class.A* readonly %a) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq %class.A* %a, null +// CHECK-NEXT: br i1 [[isnull]], label %[[dynamic_cast_end:[a-z0-9._]+]], label %[[dynamic_cast_notnull:[a-z0-9._]+]] +// CHECK: [[dynamic_cast_notnull]]: +// CHECK-NEXT: [[a:%[0-9]+]] = bitcast %class.A* %a to i8* +// CHECK-NEXT: [[as_b:%[0-9]+]] = tail call i8* @__dynamic_cast(i8* nonnull [[a]], i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*), i8* bitcast ({ i8*, i8*, i8* }* @_ZTI1B to i8*), i64 0) +// CHECK-NEXT: [[b:%[0-9]+]] = bitcast i8* [[as_b]] to %class.B* +// CHECK-NEXT: br label %[[dynamic_cast_end]] +// CHECK: [[dynamic_cast_end]]: +// CHECK-NEXT: [[res:%[0-9]+]] = phi %class.B* [ [[b]], %[[dynamic_cast_notnull]] ], [ null, %entry ] +// CHECK-NEXT: ret %class.B* [[res]] +// CHECK-NEXT: } + +// CHECK: declare i8* @__dynamic_cast(i8*, i8*, i8*, i64) local_unnamed_addr + +// CHECK: define dso_local %class.B* @_Z8selfcastP1B(%class.B* readnone returned %b) local_unnamed_addr +// CHECK-NEXT: entry +// CHECK-NEXT: ret %class.B* %b +// CHECK-NEXT: } + +// CHECK: define dso_local i8* @_Z9void_castP1B(%class.B* readonly %b) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq %class.B* %b, null +// CHECK-NEXT: br i1 [[isnull]], label %[[dynamic_cast_end:[a-z0-9._]+]], label %[[dynamic_cast_notnull:[a-z0-9._]+]] +// CHECK: [[dynamic_cast_notnull]]: +// CHECK-NEXT: [[b:%[0-9]+]] = bitcast %class.B* %b to i8* +// CHECK-NEXT: [[b2:%[0-9]+]] = bitcast %class.B* %b to i64** +// CHECK-NEXT: [[vtable:%[a-z0-9]+]] = load i64*, i64** [[b2]], align 8 +// CHECK-NEXT: [[offset_ptr:%.+]] = getelementptr inbounds i64, i64* [[vtable]], i64 -2 +// CHECK-NEXT: [[offset_to_top:%.+]] = load i64, i64* [[offset_ptr]], align 8 +// CHECK-NEXT: [[casted:%.+]] = getelementptr inbounds i8, i8* [[b]], i64 [[offset_to_top]] +// CHECK-NEXT: br label %[[dynamic_cast_end]] +// CHECK: [[dynamic_cast_end]]: +// CHECK-NEXT: [[res:%[0-9]+]] = phi i8* [ [[casted]], %[[dynamic_cast_notnull]] ], [ null, %entry ] +// CHECK-NEXT: ret i8* [[res]] +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); +}; + +class B : public A { +public: + void foo() override; +}; + +void A::foo() {} +void B::foo() {} + +A *upcast(B *b) { + return dynamic_cast(b); +} + +B *downcast(A *a) { + return dynamic_cast(a); +} + +B *selfcast(B *b) { + return dynamic_cast(b); +} + +void *void_cast(B *b) { + return dynamic_cast(b); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/inheritted-virtual-function.cpp b/clang/test/CodeGenCXX/Fuchsia/inheritted-virtual-function.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/inheritted-virtual-function.cpp @@ -0,0 +1,28 @@ +// Check the layout of the vtable for a child class that inherits a virtual +// function but does not override it. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +class A { +public: + virtual void foo(); +}; + +// The VTable for B should look similar to the vtable for A but the component for foo() should point to A::foo() and the component for bar() should point to B::bar(). +// CHECK: @_ZTV1B = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +class B : public A { +public: + virtual void bar(); +}; + +void A::foo() {} +void B::bar() {} + +void A_foo(A *a) { + a->foo(); +} + +void B_foo(B *b) { + b->foo(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/inline-virtual-function.cpp b/clang/test/CodeGenCXX/Fuchsia/inline-virtual-function.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/inline-virtual-function.cpp @@ -0,0 +1,25 @@ +// The VTable is not in a comdat but the inline methods are. +// This doesn’t affect the vtable or the stubs we emit. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZN1A3barEv.stub = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any + +// The vtable has a key function (A::foo()) so it does not have a comdat +// CHECK: @_ZTV1A = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// We do not declare the stub with linkonce_odr so it can be emitted as .globl. +// CHECK: define hidden void @_ZN1A3barEv.stub(%class.A* nocapture readnone %0) unnamed_addr #{{[0-9]+}} comdat { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); // Key func + inline virtual void bar() {} +}; + +void A::foo() {} diff --git a/clang/test/CodeGenCXX/Fuchsia/inlined-key-function.cpp b/clang/test/CodeGenCXX/Fuchsia/inlined-key-function.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/inlined-key-function.cpp @@ -0,0 +1,31 @@ +// Inline comdat method definition example. +// The VTable is in a comdat and defined anywhere the inline definition is. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZTV1A = comdat any +// CHECK: $_ZTS1A = comdat any +// CHECK: $_ZTI1A = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any + +// The VTable is linkonce_odr and in a comdat here bc it’s key function is inline defined. +// CHECK: @_ZTV1A = linkonce_odr dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat, align 8 + +// CHECK: define hidden void @_ZN1A3fooEv.stub(%class.A* nocapture readnone %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); +}; +void A_foo(A *a); + +inline void A::foo() {} + +void func() { + A a; + A_foo(&a); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/member-function-pointer.cpp b/clang/test/CodeGenCXX/Fuchsia/member-function-pointer.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/member-function-pointer.cpp @@ -0,0 +1,47 @@ +// Member pointer to virtual function. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O3 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: define dso_local void @_Z4funcP1AMS_FvvE(%class.A* %a, [2 x i64] %fn.coerce) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[fn_ptr:%.+]] = extractvalue [2 x i64] %fn.coerce, 0 +// CHECK-NEXT: [[adjust:%.+]] = extractvalue [2 x i64] %fn.coerce, 1 +// CHECK-NEXT: [[this:%.+]] = bitcast %class.A* %a to i8* +// CHECK-NEXT: [[this_adj:%.+]] = getelementptr inbounds i8, i8* [[this]], i64 [[adjust]] +// CHECK-NEXT: [[a:%.+]] = bitcast i8* [[this_adj]] to %class.A* +// CHECK-NEXT: [[virtbit:%.+]] = and i64 [[fn_ptr]], 1 +// CHECK-NEXT: [[isvirt:%.+]] = icmp eq i64 [[virtbit]], 0 +// CHECK-NEXT: br i1 [[isvirt]], label %[[nonvirt:.+]], label %[[virt:.+]] +// CHECK: [[virt]]: + +// The loading of the virtual function here should be replaced with a llvm.load.relative() call. +// CHECK-NEXT: [[this:%.+]] = bitcast i8* [[this_adj]] to i8** +// CHECK-NEXT: [[vtable:%.+]] = load i8*, i8** [[this]], align 8 +// CHECK-NEXT: [[offset:%.+]] = add i64 [[fn_ptr]], -1 +// CHECK-NEXT: [[ptr:%.+]] = tail call i8* @llvm.load.relative.i64(i8* [[vtable]], i64 [[offset]]) +// CHECK-NEXT: [[method:%.+]] = bitcast i8* [[ptr]] to void (%class.A*)* +// CHECK-NEXT: br label %[[memptr_end:.+]] +// CHECK: [[nonvirt]]: +// CHECK-NEXT: [[method2:%.+]] = inttoptr i64 [[fn_ptr]] to void (%class.A*)* +// CHECK-NEXT: br label %[[memptr_end]] +// CHECK: [[memptr_end]]: +// CHECK-NEXT: [[method3:%.+]] = phi void (%class.A*)* [ [[method]], %[[virt]] ], [ [[method2]], %[[nonvirt]] ] +// CHECK-NEXT: tail call void [[method3]](%class.A* [[a]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); +}; + +class B : public A { +public: + void foo() override; +}; + +typedef void (A::*A_foo)(); + +void func(A *a, A_foo fn) { + (a->*fn)(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/multiple-inheritance.cpp b/clang/test/CodeGenCXX/Fuchsia/multiple-inheritance.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/multiple-inheritance.cpp @@ -0,0 +1,53 @@ +// Multiple inheritance. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: %class.C = type { %class.A, %class.B } +// CHECK: %class.A = type { i32 (...)** } +// CHECK: %class.B = type { i32 (...)** } + +// VTable for C contains 2 sub-vtables (represented as 2 structs). The first contains the components for B and the second contains the components for C. The RTTI ptr in both arrays still point to the RTTI struct for C. +// The component for bar() instead points to a thunk which redirects to C::bar() which overrides B::bar(). +// Now that we have a class with 2 parents, the offset to top in the second array is non-zero. +// CHECK: @_ZTV1C = dso_local unnamed_addr constant { { i8*, i32, i32, i32 }, { i8*, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 }, { i8*, i32, i32 } }, { { i8*, i32, i32, i32 }, { i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZN1C3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 }, { i8*, i32, i32 } }, { { i8*, i32, i32, i32 }, { i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZN1C3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 }, { i8*, i32, i32 } }, { { i8*, i32, i32, i32 }, { i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 0, i32 2) to i64)) to i32) }, { i8*, i32, i32 } { i8* inttoptr (i64 -8 to i8*), i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 }, { i8*, i32, i32 } }, { { i8*, i32, i32, i32 }, { i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* @_ZThn8_N1C3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 }, { i8*, i32, i32 } }, { { i8*, i32, i32, i32 }, { i8*, i32, i32 } }* @_ZTV1C, i32 0, i32 1, i32 2) to i64)) to i32) } }, align 8 + +// CHECK: define dso_local void @_Z8C_foobarP1C(%class.C* %c) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[c:%[0-9]+]] = bitcast %class.C* %c to i8** +// CHECK-NEXT: [[vtable:%[a-z0-9]+]] = load i8*, i8** [[c]], align 8 + +// Offset 0 to get first method +// CHECK-NEXT: [[ptr1:%[0-9]+]] = call i8* @llvm.load.relative.i32(i8* [[vtable]], i32 0) +// CHECK-NEXT: [[method1:%[0-9]+]] = bitcast i8* [[ptr1]] to void (%class.C*)* +// CHECK-NEXT: call void [[method1]](%class.C* %c) +// CHECK-NEXT: [[vtable:%[a-z0-9]+]] = load i8*, i8** [[c]], align 8 + +// Offset by 4 to get the next bar() +// CHECK-NEXT: [[ptr2:%[0-9]+]] = call i8* @llvm.load.relative.i32(i8* [[vtable]], i32 4) +// CHECK-NEXT: [[method2:%[0-9]+]] = bitcast i8* [[ptr2]] to void (%class.C*)* +// CHECK-NEXT: call void [[method2]](%class.C* %c) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); +}; + +class B { + virtual void bar(); +}; + +class C : public A, public B { +public: + void foo() override; + void bar() override; +}; + +void C::foo() {} +void C::bar() {} + +void C_foobar(C *c) { + c->foo(); + c->bar(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/override-pure-virtual-method.cpp b/clang/test/CodeGenCXX/Fuchsia/override-pure-virtual-method.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/override-pure-virtual-method.cpp @@ -0,0 +1,31 @@ +// Override pure virtual function. +// We instead emit zero for the pure virtual function component. See PR43094 for +// details. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: @_ZTV1A = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 0, i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// CHECK: @_ZTV1B = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +// CHECK-NOT: declare void @__cxa_pure_virtual() unnamed_addr + +class A { +public: + virtual void foo() = 0; + virtual void bar(); +}; + +class B : public A { +public: + void foo() override; + void bar() override; +}; + +void A::bar() {} +void B::foo() {} +void B::bar() {} + +void A_foo(A *a) { + a->foo(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/overriden-virtual-function.cpp b/clang/test/CodeGenCXX/Fuchsia/overriden-virtual-function.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/overriden-virtual-function.cpp @@ -0,0 +1,28 @@ +// Check the layout of the vtable for a child class that inherits a virtual +// function but does override it. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: @_ZTV1B = dso_local unnamed_addr constant { { i8*, i32, i32, i32 } } { { i8*, i32, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3barEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32, i32 } }, { { i8*, i32, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 + +class A { +public: + virtual void foo(); +}; + +class B : public A { +public: + void foo() override; + virtual void bar(); +}; + +void A::foo() {} +void B::foo() {} + +void A_foo(A *a) { + a->foo(); +} + +void B_foo(B *b) { + b->foo(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/parent-and-child-in-comdats.cpp b/clang/test/CodeGenCXX/Fuchsia/parent-and-child-in-comdats.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/parent-and-child-in-comdats.cpp @@ -0,0 +1,59 @@ +// Cross comdat example +// Both the parent and child VTablea are in their own comdat sections. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// Comdats are emitted for both A and B in this module and for their respective implementations of foo(). +// CHECK: $_ZN1A3fooEv = comdat any +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZN1B3fooEv = comdat any +// CHECK: $_ZN1B3fooEv.stub = comdat any +// CHECK: $_ZTV1A = comdat any +// CHECK: $_ZTS1A = comdat any +// CHECK: $_ZTI1A = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any +// CHECK: $_ZTV1B = comdat any +// CHECK: $_ZTS1B = comdat any +// CHECK: $_ZTI1B = comdat any +// CHECK: $_ZTI1B.rtti_proxy = comdat any + +// Both the vtables for A and B are emitted and in their own comdats. +// CHECK: @_ZTV1A = linkonce_odr dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat, align 8 +// CHECK: @_ZTV1B = linkonce_odr dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* @_ZN1B3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1B, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat, align 8 + +// CHECK: declare void @_Z5A_fooP1A(%class.A*) + +// The stubs and implementations for foo() are in their own comdat sections. +// CHECK: define linkonce_odr dso_local void @_ZN1A3fooEv(%class.A* %this) unnamed_addr #{{[0-9]+}} comdat + +// CHECK: define hidden void @_ZN1A3fooEv.stub(%class.A* %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @_ZN1A3fooEv(%class.A* %0) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define linkonce_odr dso_local void @_ZN1B3fooEv(%class.B* %this) unnamed_addr #{{[0-9]+}} comdat + +// CHECK: define hidden void @_ZN1B3fooEv.stub(%class.B* %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @_ZN1B3fooEv(%class.B* %0) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + inline virtual void foo() {} +}; +class B : public A { +public: + inline void foo() override {} +}; +void A_foo(A *a); + +// func() is used so that the vtable for B is accessed when creating the instance. +void func() { + A a; + B b; + A_foo(&a); + A_foo(&b); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/parent-vtable-in-comdat.cpp b/clang/test/CodeGenCXX/Fuchsia/parent-vtable-in-comdat.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/parent-vtable-in-comdat.cpp @@ -0,0 +1,47 @@ +// Cross comdat example +// Parent VTable is in a comdat section. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// A::foo() has a comdat since it is an inline function +// CHECK: $_ZN1A3fooEv = comdat any +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZTV1A = comdat any +// CHECK: $_ZTS1A = comdat any + +// The VTable for A has its own comdat section bc it has no key function +// CHECK: $_ZTI1A = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any + +// The VTable for A is emitted here and in a comdat section since it has no key function, and is used in this module when creating an instance of A. +// CHECK: @_ZTV1A = linkonce_odr dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat, align 8 +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global i8* +// CHECK: @_ZTS1A = linkonce_odr dso_local constant [3 x i8] c"1A\00", comdat, align 1 +// CHECK: @_ZTI1A = linkonce_odr dso_local constant { i8*, i8* } { i8* getelementptr inbounds (i8, i8* bitcast (i8** @_ZTVN10__cxxabiv117__class_type_infoE to i8*), i32 12), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1A, i32 0, i32 0) }, comdat, align 8 +// CHECK: @_ZTI1A.rtti_proxy = hidden unnamed_addr constant { i8*, i8* }* @_ZTI1A, comdat + +// CHECK: define linkonce_odr dso_local void @_ZN1A3fooEv(%class.A* %this) unnamed_addr #{{[0-9]+}} comdat + +// CHECK: define hidden void @_ZN1A3fooEv.stub(%class.A* %0) unnamed_addr #{{[0-9]+}} comdat { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @_ZN1A3fooEv(%class.A* %0) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + inline virtual void foo() {} +}; +class B : public A { +public: + void foo() override; +}; +void A_foo(A *a); + +void A_foo(A *a) { a->foo(); } + +// func() is only used to emit a vtable. +void func() { + A a; + A_foo(&a); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/pass-byval-attributes.cpp b/clang/test/CodeGenCXX/Fuchsia/pass-byval-attributes.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/pass-byval-attributes.cpp @@ -0,0 +1,79 @@ +// ByVal attributes should propogate through to produce proper assembly and +// avoid "unpacking" structs within the stubs on x86_64. + +// RUN: %clang_cc1 %s -triple=x86_64-unknown-fuchsia -std=c++17 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +typedef unsigned long long uint64_t; +typedef unsigned int size_t; + +struct LargeStruct { + char x[24]; + virtual ~LargeStruct() {} +}; + +typedef struct fidl_string { + // Number of UTF-8 code units (bytes), must be 0 if |data| is null. + uint64_t size; + + // Pointer to UTF-8 code units (bytes) or null + char *data; +} fidl_string_t; + +class StringView final : private fidl_string_t { +public: + StringView() : fidl_string_t{} {} + constexpr StringView(const char *data, uint64_t size) + : fidl_string_t{size, const_cast(data)} {} + + // Constructs a fidl::StringView referencing a string literal. For example: + // + // fidl::StringView view("hello"); + // view.size() == 5; + // + template + constexpr explicit StringView(const char (&literal)[N]) + : fidl_string_t{N - 1, const_cast(literal)} { + static_assert(N > 0, "Empty string should be null-terminated"); + } + + uint64_t size() const { return fidl_string_t::size; } + void set_size(uint64_t size) { fidl_string_t::size = size; } + + const char *data() const { return fidl_string_t::data; } + void set_data(const char *data) { fidl_string_t::data = const_cast(data); } + + bool is_null() const { return fidl_string_t::data == nullptr; } + bool empty() const { return fidl_string_t::size == 0; } + + const char &at(size_t offset) const { return data()[offset]; } + + const char &operator[](size_t offset) const { return at(offset); } + + const char *begin() const { return data(); } + const char *cbegin() const { return data(); } + + const char *end() const { return data() + size(); } + const char *cend() const { return data() + size(); } +}; + +class Base { +public: + virtual void func(LargeStruct, StringView, LargeStruct, StringView) = 0; +}; + +class Derived : public Base { +public: + void func(LargeStruct, StringView, LargeStruct, StringView) override; +}; + +// The original function takes a byval pointer. +// CHECK: define dso_local void @_ZN7Derived4funcE11LargeStruct10StringViewS0_S1_(%class.Derived* %this, %struct.LargeStruct* %ls, i64 %sv1.coerce0, i8* %sv1.coerce1, %struct.LargeStruct* %ls2, %class.StringView* byval(%class.StringView) align 8 %sv2) unnamed_addr + +// So the stub should take and pass one also. +// CHECK: define hidden void @_ZN7Derived4funcE11LargeStruct10StringViewS0_S1_.stub(%class.Derived* %0, %struct.LargeStruct* %1, i64 %2, i8* %3, %struct.LargeStruct* %4, %class.StringView* byval(%class.StringView) align 8 %5) unnamed_addr {{#[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @_ZN7Derived4funcE11LargeStruct10StringViewS0_S1_(%class.Derived* %0, %struct.LargeStruct* %1, i64 %2, i8* %3, %struct.LargeStruct* %4, %class.StringView* byval(%class.StringView) align 8 %5) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void Derived::func(LargeStruct ls, StringView sv1, LargeStruct ls2, StringView sv2) {} diff --git a/clang/test/CodeGenCXX/Fuchsia/simple-vtable-definition.cpp b/clang/test/CodeGenCXX/Fuchsia/simple-vtable-definition.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/simple-vtable-definition.cpp @@ -0,0 +1,38 @@ +// Check the layout of the vtable for a normal class. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// We should be emitting comdats for each of the virtual function stubs and RTTI proxies +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any + +// VTable contains offsets and references to the hidden symbols +// CHECK: @_ZTV1A = dso_local unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* null, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* @_ZN1A3fooEv.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV1A, i32 0, i32 0, i32 2) to i64)) to i32) } }, align 8 +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global i8* +// CHECK: @_ZTS1A = dso_local constant [3 x i8] c"1A\00", align 1 +// CHECK: @_ZTI1A = dso_local constant { i8*, i8* } { i8* getelementptr inbounds (i8, i8* bitcast (i8** @_ZTVN10__cxxabiv117__class_type_infoE to i8*), i32 12), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1A, i32 0, i32 0) }, align 8 + +// The stub should be in a comdat +// CHECK: @_ZTI1A.rtti_proxy = hidden unnamed_addr constant { i8*, i8* }* @_ZTI1A, comdat + +// CHECK: define dso_local void @_ZN1A3fooEv(%class.A* nocapture %this) unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// This function should be in a comdat +// CHECK: define hidden void @_ZN1A3fooEv.stub(%class.A* nocapture %0) unnamed_addr #{{[0-9]+}} comdat +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); +}; + +void A::foo() {} + +void A_foo(A *a) { + a->foo(); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/stub-linkages.cpp b/clang/test/CodeGenCXX/Fuchsia/stub-linkages.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/stub-linkages.cpp @@ -0,0 +1,49 @@ +// If the linkage of the class is internal, then the stubs and proxies should +// also be internally linked. + +// RUN: %clang_cc1 %s -triple=x86_64-unknown-fuchsia -std=c++17 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// External linkage. +// CHECK: @_ZTV8External = dso_local unnamed_addr constant +// CHECK: @_ZTI8External.rtti_proxy = hidden unnamed_addr constant { i8*, i8* }* @_ZTI8External, comdat + +class External { +public: + virtual void func(); +}; + +void External::func() {} + +// Internal linkage. +// CHECK: @_ZTVN12_GLOBAL__N_18InternalE = internal unnamed_addr constant +// CHECK: @_ZTIN12_GLOBAL__N_18InternalE.rtti_proxy = internal unnamed_addr constant { i8*, i8* }* @_ZTIN12_GLOBAL__N_18InternalE +namespace { + +class Internal { +public: + virtual void func(); +}; + +void Internal::func() {} + +} // namespace + +// This gets the same treatment as an externally available vtable. +// CHECK: @_ZTV11LinkOnceODR = linkonce_odr dso_local unnamed_addr constant +// CHECK: @_ZTI11LinkOnceODR.rtti_proxy = hidden unnamed_addr constant { i8*, i8* }* @_ZTI11LinkOnceODR, comdat +class LinkOnceODR { +public: + virtual void func() {} // A method defined in the class definition results in this linkage for the vtable. +}; + +// Force an emission of a vtable for Internal by using it here. +void manifest_internal() { + Internal internal; + (void)internal; + LinkOnceODR linkonceodr; + (void)linkonceodr; +} + +// CHECK: define dso_local void @_ZN8External4funcEv +// CHECK: define internal void @_ZN12_GLOBAL__N_18Internal4funcEv.stub +// CHECK: define hidden void @_ZN11LinkOnceODR4funcEv.stub diff --git a/clang/test/CodeGenCXX/Fuchsia/thunk-mangling.cpp b/clang/test/CodeGenCXX/Fuchsia/thunk-mangling.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/thunk-mangling.cpp @@ -0,0 +1,30 @@ +// Check that virtual thunks are unaffected by the relative ABI. +// The offset of thunks is mangled into the symbol name, which could result in +// linking errors for binaries that want to look for symbols in SOs made with +// this ABI. +// Running that linked binary still won't work since we're using conflicting +// ABIs, but we should still be able to link. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O1 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// This would be n24 under Itanium +// CHECK: _ZTv0_n20_N7Derived1fEi + +class Base { +public: + virtual int f(int x); + +private: + long x; +}; + +class Derived : public virtual Base { +public: + virtual int f(int x); + +private: + long y; +}; + +int Base::f(int x) { return x + 1; } +int Derived::f(int x) { return x + 2; } diff --git a/clang/test/CodeGenCXX/Fuchsia/type-info.cpp b/clang/test/CodeGenCXX/Fuchsia/type-info.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/type-info.cpp @@ -0,0 +1,77 @@ +// Check typeid() + type_info + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O3 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm -fcxx-exceptions -fexceptions | FileCheck %s + +// CHECK: %class.A = type { i32 (...)** } +// CHECK: %class.B = type { %class.A } +// CHECK: %"class.std::type_info" = type { i32 (...)**, i8* } + +// CHECK: $_ZN1A3fooEv.stub = comdat any +// CHECK: $_ZN1B3fooEv.stub = comdat any +// CHECK: $_ZTI1A.rtti_proxy = comdat any +// CHECK: $_ZTI1B.rtti_proxy = comdat any + +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global i8* +// CHECK: @_ZTS1A = dso_local constant [3 x i8] c"1A\00", align 1 +// CHECK: @_ZTI1A = dso_local constant { i8*, i8* } { i8* getelementptr inbounds (i8, i8* bitcast (i8** @_ZTVN10__cxxabiv117__class_type_infoE to i8*), i32 12), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1A, i32 0, i32 0) }, align 8 +// CHECK: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global i8* +// CHECK: @_ZTS1B = dso_local constant [3 x i8] c"1B\00", align 1 +// CHECK: @_ZTI1B = dso_local constant { i8*, i8*, i8* } { i8* getelementptr inbounds (i8, i8* bitcast (i8** @_ZTVN10__cxxabiv120__si_class_type_infoE to i8*), i32 12), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1B, i32 0, i32 0), i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*) }, align 8 +// CHECK: @_ZTI1A.rtti_proxy = hidden unnamed_addr constant { i8*, i8* }* @_ZTI1A, comdat +// CHECK: @_ZTI1B.rtti_proxy = hidden unnamed_addr constant { i8*, i8*, i8* }* @_ZTI1B, comdat + +// CHECK: define dso_local dereferenceable(16) %"class.std::type_info"* @_Z11getTypeInfov() local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: ret %"class.std::type_info"* bitcast ({ i8*, i8* }* @_ZTI1A to %"class.std::type_info"*) +// CHECK-NEXT: } + +// CHECK: define dso_local i8* @_Z7getNamev() local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1A, i64 0, i64 0) +// CHECK-NEXT: } + +// CHECK: define dso_local i1 @_Z5equalP1A(%class.A* readonly %a) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq %class.A* %a, null +// CHECK-NEXT: br i1 [[isnull]], label %[[bad_typeid:[a-z0-9._]+]], label %[[end:[a-z0-9.+]+]] +// CHECK: [[bad_typeid]]: +// CHECK-NEXT: tail call void @__cxa_bad_typeid() +// CHECK-NEXT: unreachable +// CHECK: [[end]]: +// CHECK-NEXT: [[type_info_ptr3:%[0-9]+]] = bitcast %class.A* %a to i8** +// CHECK-NEXT: [[vtable:%[a-z0-9]+]] = load i8*, i8** [[type_info_ptr3]] +// CHECK-NEXT: [[type_info_ptr:%[0-9]+]] = tail call i8* @llvm.load.relative.i32(i8* [[vtable]], i32 -4) +// CHECK-NEXT: [[type_info_ptr2:%[0-9]+]] = bitcast i8* [[type_info_ptr]] to %"class.std::type_info"** +// CHECK-NEXT: [[type_info_ptr:%[0-9]+]] = load %"class.std::type_info"*, %"class.std::type_info"** [[type_info_ptr2]], align 8 +// CHECK-NEXT: [[name_ptr:%[a-z0-9._]+]] = getelementptr inbounds %"class.std::type_info", %"class.std::type_info"* [[type_info_ptr]], i64 0, i32 1 +// CHECK-NEXT: [[name:%[0-9]+]] = load i8*, i8** [[name_ptr]], align 8 +// CHECK-NEXT: [[eq:%[a-z0-9.]+]] = icmp eq i8* [[name]], getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1B, i64 0, i64 0) +// CHECK-NEXT: ret i1 [[eq]] +// CHECK-NEXT: } + +#include "../typeinfo" + +class A { +public: + virtual void foo(); +}; + +class B : public A { +public: + void foo() override; +}; + +void A::foo() {} +void B::foo() {} + +const auto &getTypeInfo() { + return typeid(A); +} + +const char *getName() { + return typeid(A).name(); +} + +bool equal(A *a) { + return typeid(B) == typeid(*a); +} diff --git a/clang/test/CodeGenCXX/Fuchsia/virtual-function-call.cpp b/clang/test/CodeGenCXX/Fuchsia/virtual-function-call.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Fuchsia/virtual-function-call.cpp @@ -0,0 +1,22 @@ +// Check that we call llvm.load.relative() on a vtable function call. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -std=c++17 -O3 -pic-is-pie -ffunction-sections -fdata-sections -S -o - -emit-llvm | FileCheck %s + +// CHECK: define dso_local void @_Z5A_fooP1A(%class.A* %a) local_unnamed_addr +// CHECK-NEXT: entry: +// CHECK-NEXT: [[this:%[0-9]+]] = bitcast %class.A* %a to i8** +// CHECK-NEXT: %vtable1 = load i8*, i8** [[this]] +// CHECK-NEXT: [[func_ptr:%[0-9]+]] = tail call i8* @llvm.load.relative.i32(i8* %vtable1, i32 0) +// CHECK-NEXT: [[func:%[0-9]+]] = bitcast i8* [[func_ptr]] to void (%class.A*)* +// CHECK-NEXT: tail call void [[func]](%class.A* %a) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +class A { +public: + virtual void foo(); +}; + +void A_foo(A *a) { + a->foo(); +} diff --git a/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp b/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp --- a/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp +++ b/clang/test/CodeGenCXX/constructor-destructor-return-this.cpp @@ -151,8 +151,11 @@ // Verify that virtual calls to destructors are not marked with a 'returned' // this parameter at the call site... -// CHECKARM,CHECKFUCHSIA: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)*, %class.E* (%class.E*)** -// CHECKARM,CHECKFUCHSIA: [[THUNK:%.*]] = load %class.E* (%class.E*)*, %class.E* (%class.E*)** [[VFN]] +// CHECKARM: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)*, %class.E* (%class.E*)** +// CHECKARM: [[THUNK:%.*]] = load %class.E* (%class.E*)*, %class.E* (%class.E*)** [[VFN]] +// CHECKFUCHSIA: [[VTABLE:%.+]] = bitcast %class.E* (%class.E*)** +// CHECKFUCHSIA: [[THUNK_PTR:%.+]] = call i8* @llvm.load.relative.i32(i8* [[VTABLE]], i32 0) +// CHECKFUCHSIA: [[THUNK:%.+]] = bitcast i8* [[THUNK_PTR]] to %class.E* (%class.E*)* // CHECKARM,CHECKFUCHSIA: call %class.E* [[THUNK]](%class.E* % // ...but static calls create declarations with 'returned' this diff --git a/libcxxabi/src/private_typeinfo.cpp b/libcxxabi/src/private_typeinfo.cpp --- a/libcxxabi/src/private_typeinfo.cpp +++ b/libcxxabi/src/private_typeinfo.cpp @@ -614,10 +614,24 @@ // Possible future optimization: Take advantage of src2dst_offset // Get (dynamic_ptr, dynamic_type) from static_ptr +#ifndef __Fuchsia__ void **vtable = *static_cast(static_ptr); ptrdiff_t offset_to_derived = reinterpret_cast(vtable[-2]); const void* dynamic_ptr = static_cast(static_ptr) + offset_to_derived; const __class_type_info* dynamic_type = static_cast(vtable[-1]); +#else + // The vtable address will point to the first virtual function, which is 12 + // bytes after the start of the vtable (8 for the offset from top + 4 for the typeinfo component). + uint8_t *vtable = *reinterpret_cast(static_ptr); + ptrdiff_t offset_to_derived = *(reinterpret_cast(vtable - 12)); + + const void* dynamic_ptr = static_cast(static_ptr) + offset_to_derived; + + // The typeinfo component is now a relative offset to a proxy. + int32_t offset_to_ti_proxy = *reinterpret_cast(vtable - 4); + uint8_t *ptr_to_ti_proxy = vtable + offset_to_ti_proxy; + const __class_type_info* dynamic_type = *(reinterpret_cast<__class_type_info **>(ptr_to_ti_proxy)); +#endif // Initialize answer to nullptr. This will be changed from the search // results if a non-null answer is found. Regardless, this is what will diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -460,7 +460,7 @@ // assumed it. if (TM.getTargetTriple().isOSBinFormatELF() && GlobalObject::isExternalLinkage(GV.getLinkage()) && GV.isDSOLocal() && - !GV.isDeclaration() && !isa(GV)) + !GV.isDeclaration() && !isa(GV) && !GV.hasComdat()) return getSymbolWithGlobalValueBase(&GV, "$local"); return TM.getSymbol(&GV); } diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -571,8 +571,10 @@ return false; // Relative pointers do not need to be dynamically relocated. - if (auto *LHSGV = dyn_cast(LHSOp0->stripPointerCasts())) - if (auto *RHSGV = dyn_cast(RHSOp0->stripPointerCasts())) + if (auto *LHSGV = + dyn_cast(LHSOp0->stripInBoundsConstantOffsets())) + if (auto *RHSGV = + dyn_cast(RHSOp0->stripInBoundsConstantOffsets())) if (LHSGV->isDSOLocal() && RHSGV->isDSOLocal()) return false; } diff --git a/llvm/test/CodeGen/X86/relptr-rodata.ll b/llvm/test/CodeGen/X86/relptr-rodata.ll --- a/llvm/test/CodeGen/X86/relptr-rodata.ll +++ b/llvm/test/CodeGen/X86/relptr-rodata.ll @@ -19,3 +19,27 @@ ; CHECK: relro2: ; CHECK: .long hidden-relro2 @relro2 = constant i32 trunc (i64 sub (i64 ptrtoint (i8* @hidden to i64), i64 ptrtoint (i32* @relro2 to i64)) to i32) + +; CHECK: .section .rodata.cst16 +; CHECK-NEXT: .globl _ZTV4Test +; CHECK: _ZTV4Test +; CHECK: .quad 0 +; CHECK: .long (_ZTI4Test.rtti_proxy-_ZTV4Test)-8 +; CHECK: .long (_ZN4TestD1Ev.stub@PLT-_ZTV4Test)-12 + +@_ZTI4Test = external global i8 +@_ZTI4Test.rtti_proxy = hidden unnamed_addr constant i8* @_ZTI4Test + +declare void @_ZN4TestD2Ev() +define hidden void @_ZN4TestD1Ev.stub() unnamed_addr { +entry: + tail call void @_ZN4TestD2Ev() + ret void +} + +@_ZTV4Test = dso_local unnamed_addr constant { { i8*, i32, i32 } } { + { i8*, i32, i32 } { + i8* null, + i32 trunc (i64 sub (i64 ptrtoint (i8** @_ZTI4Test.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV4Test, i32 0, i32 0, i32 1) to i64)) to i32), + i32 trunc (i64 sub (i64 ptrtoint (void ()* @_ZN4TestD1Ev.stub to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @_ZTV4Test, i32 0, i32 0, i32 2) to i64)) to i32) + } }, align 8