Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ llvm/lib/LTO/LTO.cpp @@ -228,6 +228,55 @@ Hasher.update(FileOrErr.get()->getBuffer()); } + // Include the hashes of all members of cfiFunctionDefs and cfiFunctionDecls + // that are referenced or defined in this module. + DenseMap UsedCfiDefs; + DenseMap UsedCfiDecls; + unsigned UsedCfiDefsCount = 0; + unsigned UsedCfiDeclsCount = 0; + + for (const std::string& Name : Index.cfiFunctionDefs()) + UsedCfiDefs[GlobalValue::getGUID(Name)] = false; + for (const std::string& Name : Index.cfiFunctionDecls()) + UsedCfiDecls[GlobalValue::getGUID(Name)] = false; + + auto AddUsedCfiGlobal = [&](GlobalValue::GUID ValueGUID) { + auto DefsIt = UsedCfiDefs.find(ValueGUID); + if (DefsIt != UsedCfiDefs.end()) { + UsedCfiDefsCount += !DefsIt->second; + DefsIt->second = true; + } + auto DeclsIt = UsedCfiDecls.find(ValueGUID); + if (DeclsIt != UsedCfiDecls.end()) { + UsedCfiDeclsCount += !DeclsIt->second; + DeclsIt->second = true; + } + }; + + for (auto &GSM : DefinedGlobals) { + AddUsedCfiGlobal(GSM.first); + for (const ValueInfo &VI : GSM.second->refs()) + AddUsedCfiGlobal(VI.getGUID()); + } + + for (auto &ImpM : ImportList) + for (auto &ImpF : ImpM.second) { + AddUsedCfiGlobal(ImpF.first); // FIXME: is this necessary? + GlobalValueSummary *GS = Index.findSummaryInModule(ImpF.first, ImpM.first()); + for (const ValueInfo &VI : GS->refs()) + AddUsedCfiGlobal(VI.getGUID()); + } + + AddUnsigned(UsedCfiDefsCount); + for (auto &KV : UsedCfiDefs) + if (KV.second) + AddUint64(KV.first); + + AddUnsigned(UsedCfiDeclsCount); + for (auto &KV : UsedCfiDecls) + if (KV.second) + AddUint64(KV.first); + Key = toHex(Hasher.result()); } Index: llvm/test/ThinLTO/X86/Inputs/cache-icall-no.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/Inputs/cache-icall-no.ll @@ -0,0 +1,7 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @f() { +entry: + ret void +} Index: llvm/test/ThinLTO/X86/Inputs/cache-icall-yes.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/Inputs/cache-icall-yes.ll @@ -0,0 +1,9 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @f() !type !0 { +entry: + ret void +} + +!0 = !{i64 0, !"_ZTSFvvE"} Index: llvm/test/ThinLTO/X86/cache-icall.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/cache-icall.ll @@ -0,0 +1,57 @@ +; Test that the list of CFI jumptable entries is part of ThinLTO cache key. + +; Linking module -yes results in f() being added to CFI jumptable; with -no it is not. +; This affects code generated for any users of f(). Make sure that we don't pull a stale object +; file for %t.o from the cache when -no is replaced with -yes in the link. + +; RUN: opt -module-hash -module-summary -thinlto-bc %s -o %t.bc +; RUN: opt -module-hash -module-summary -thinlto-bc %p/Inputs/cache-icall-no.ll -o %t-no.bc +; RUN: opt -module-hash -module-summary -thinlto-bc %p/Inputs/cache-icall-yes.ll -o %t-yes.bc + +; RUN: rm -Rf %t.cache && mkdir %t.cache + +; RUN: llvm-lto2 run -o %t-no.o %t.bc %t-no.bc -cache-dir %t.cache \ +; RUN: -r=%t.bc,_start,px \ +; RUN: -r=%t.bc,f, \ +; RUN: -r=%t-no.bc,f,p + +; RUN: llvm-readelf -symbols %t-no.o.* | FileCheck %s --check-prefix=SYMBOLS-NO + +; RUN: llvm-lto2 run -o %t-yes.o %t.bc %t-yes.bc -cache-dir %t.cache \ +; RUN: -r=%t.bc,_start,px \ +; RUN: -r=%t.bc,f, \ +; RUN: -r=%t-yes.bc,f,p + +; RUN: llvm-readelf -symbols %t-yes.o.* | FileCheck %s --check-prefix=SYMBOLS-YES + +; SYMBOLS-NO-DAG: {{FUNC .* f.cfi_jt$}} +; SYMBOLS-NO-DAG: {{NOTYPE .* UND f.cfi_jt$}} + +; SYMBOLS-YES-NOT: f.cfi_jt +; SYMBOLS-YES-DAG: {{FUNC .* f.cfi$}} +; SYMBOLS-YES-DAG: {{NOTYPE .* UND f.cfi$}} + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i8* @_start(void ()* %p) !type !0 { +entry: + %0 = bitcast void ()* %p to i8* + %1 = tail call i1 @llvm.type.test(i8* %0, metadata !"_ZTSFvvE") + br i1 %1, label %cont, label %trap + +trap: ; preds = %entry + tail call void @llvm.trap() + unreachable + +cont: ; preds = %entry + tail call void %p() + ret i8* bitcast (void ()* @f to i8*) +} + +declare i1 @llvm.type.test(i8*, metadata) +declare void @llvm.trap() +declare !type !1 void @f() + +!0 = !{i64 0, !"_ZTSFPvPFvvEE"} +!1 = !{i64 0, !"_ZTSFvvE"}