diff --git a/src/irgenerator/GenVTable.cpp b/src/irgenerator/GenVTable.cpp index 49506203d..a96731215 100644 --- a/src/irgenerator/GenVTable.cpp +++ b/src/irgenerator/GenVTable.cpp @@ -27,7 +27,7 @@ llvm::Constant *IRGenerator::generateTypeInfoName(StructBase *spiceStruct) const // Set global attributes global->setConstant(true); global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::None); - global->setLinkage(getSymbolLinkageType(isPublic)); + global->setLinkage(getVTableLinkageType(isPublic)); global->setDSOLocal(isSymbolDSOLocal(isPublic)); attachComdatToSymbol(global, globalName, isPublic); @@ -83,7 +83,7 @@ llvm::Constant *IRGenerator::generateTypeInfo(StructBase *spiceStruct) const { // Set global attributes global->setConstant(true); global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::None); - global->setLinkage(getSymbolLinkageType(isPublic)); + global->setLinkage(getVTableLinkageType(isPublic)); global->setDSOLocal(isSymbolDSOLocal(isPublic)); global->setAlignment(llvm::MaybeAlign(8)); attachComdatToSymbol(global, mangledName, isPublic); @@ -113,7 +113,7 @@ llvm::Constant *IRGenerator::generateVTable(StructBase *spiceStruct) const { // Set global attributes global->setConstant(true); global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - global->setLinkage(getSymbolLinkageType(isPublic)); + global->setLinkage(getVTableLinkageType(isPublic)); global->setDSOLocal(isSymbolDSOLocal(isPublic)); global->setAlignment(llvm::MaybeAlign(8)); attachComdatToSymbol(global, mangledName, isPublic); diff --git a/src/irgenerator/IRGenerator.cpp b/src/irgenerator/IRGenerator.cpp index 4767d92cb..9c74af44d 100644 --- a/src/irgenerator/IRGenerator.cpp +++ b/src/irgenerator/IRGenerator.cpp @@ -660,6 +660,14 @@ llvm::GlobalValue::LinkageTypes IRGenerator::getSymbolLinkageType(bool isPublic) return isPublic ? llvm::GlobalValue::ExternalLinkage : llvm::GlobalValue::PrivateLinkage; } +llvm::GlobalValue::LinkageTypes IRGenerator::getVTableLinkageType(bool isPublic) const { + // VTables, type infos and type info names are ODR entities that may legitimately be emitted in more than one + // translation unit (e.g. an interface that is forward-declared in one file and fully defined in another). Giving + // them weak ODR linkage lets the linker coalesce the duplicates. On ELF this pairs with the comdat group below; on + // MachO, which has no comdat support, the weak/coalesced linkage is what prevents a duplicate-symbol error. + return isPublic ? llvm::GlobalValue::WeakODRLinkage : llvm::GlobalValue::PrivateLinkage; +} + void IRGenerator::attachComdatToSymbol(llvm::GlobalVariable *global, const std::string &comdatName, bool isPublic) const { // MachO does not support comdat annotations if (isPublic && cliOptions.targetTriple.getObjectFormat() != llvm::Triple::MachO) diff --git a/src/irgenerator/IRGenerator.h b/src/irgenerator/IRGenerator.h index 713ae58ed..f4f144b9c 100644 --- a/src/irgenerator/IRGenerator.h +++ b/src/irgenerator/IRGenerator.h @@ -179,6 +179,7 @@ class IRGenerator final : CompilerPass, public ParallelizableASTVisitor { llvm::Attribute::AttrKind getExtAttrKindForType(const QualType &type) const; bool isSymbolDSOLocal(bool isPublic) const; llvm::GlobalValue::LinkageTypes getSymbolLinkageType(bool isPublic) const; + llvm::GlobalValue::LinkageTypes getVTableLinkageType(bool isPublic) const; void attachComdatToSymbol(llvm::GlobalVariable *global, const std::string &comdatName, bool isPublic) const; // Generate implicit diff --git a/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code-O2.ll b/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code-O2.ll index 1de98984a..2fcc0df60 100644 --- a/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code-O2.ll +++ b/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code-O2.ll @@ -10,11 +10,11 @@ $_ZTI3Car = comdat any $_ZTV3Car = comdat any -@_ZTS3Car = dso_local constant [5 x i8] c"3Car\00", comdat, align 4 +@_ZTS3Car = weak_odr dso_local constant [5 x i8] c"3Car\00", comdat, align 4 @_ZTV8TypeInfo = external global ptr @_ZTI9Driveable = external global ptr -@_ZTI3Car = dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTV8TypeInfo, i64 2), ptr @_ZTS3Car, ptr @_ZTI9Driveable }, comdat, align 8 -@_ZTV3Car = dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI3Car, ptr @_ZN3Car5driveEi, ptr @_ZN3Car9isDrivingEv] }, comdat, align 8 +@_ZTI3Car = weak_odr dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTV8TypeInfo, i64 2), ptr @_ZTS3Car, ptr @_ZTI9Driveable }, comdat, align 8 +@_ZTV3Car = weak_odr dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI3Car, ptr @_ZN3Car5driveEi, ptr @_ZN3Car9isDrivingEv] }, comdat, align 8 @printf.str.0 = private unnamed_addr constant [15 x i8] c"Is driving: %d\00", align 4 ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) diff --git a/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code.ll b/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code.ll index ab2fba93d..52e43183c 100644 --- a/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code.ll +++ b/test/test-files/irgenerator/interfaces/success-cross-sourcefile-interfaces/ir-code.ll @@ -10,11 +10,11 @@ $_ZTI3Car = comdat any $_ZTV3Car = comdat any -@_ZTS3Car = dso_local constant [5 x i8] c"3Car\00", comdat, align 4 +@_ZTS3Car = weak_odr dso_local constant [5 x i8] c"3Car\00", comdat, align 4 @_ZTV8TypeInfo = external global ptr @_ZTI9Driveable = external global ptr -@_ZTI3Car = dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTV8TypeInfo, i64 2), ptr @_ZTS3Car, ptr @_ZTI9Driveable }, comdat, align 8 -@_ZTV3Car = dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI3Car, ptr @_ZN3Car5driveEi, ptr @_ZN3Car9isDrivingEv] }, comdat, align 8 +@_ZTI3Car = weak_odr dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTV8TypeInfo, i64 2), ptr @_ZTS3Car, ptr @_ZTI9Driveable }, comdat, align 8 +@_ZTV3Car = weak_odr dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI3Car, ptr @_ZN3Car5driveEi, ptr @_ZN3Car9isDrivingEv] }, comdat, align 8 @printf.str.0 = private unnamed_addr constant [15 x i8] c"Is driving: %d\00", align 4 define private void @_ZN3Car4ctorEv(ptr noundef nonnull align 8 dereferenceable(16) %0) {