diff --git a/compiler-rt/test/lsan/TestCases/suppressions_default.cpp b/compiler-rt/test/lsan/TestCases/suppressions_default.cpp --- a/compiler-rt/test/lsan/TestCases/suppressions_default.cpp +++ b/compiler-rt/test/lsan/TestCases/suppressions_default.cpp @@ -9,7 +9,7 @@ extern "C" const char *__lsan_default_suppressions() { - return "leak:*LSanTestLeakingFunc*"; + return "leak:LSanTestLeakingFunc() /"; } void LSanTestLeakingFunc() { @@ -24,5 +24,4 @@ return 0; } // CHECK: Suppressions used: -// CHECK: 1 666 *LSanTestLeakingFunc* // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -396,7 +396,6 @@ void initInlineSourceManager(); SourceMgr *getInlineSourceManager() { - assert(InlineSrcMgr); return InlineSrcMgr.get(); } std::vector &getLocInfos() { return LocInfos; } diff --git a/llvm/include/llvm/MC/MCParser/MCAsmParser.h b/llvm/include/llvm/MC/MCParser/MCAsmParser.h --- a/llvm/include/llvm/MC/MCParser/MCAsmParser.h +++ b/llvm/include/llvm/MC/MCParser/MCAsmParser.h @@ -182,6 +182,8 @@ virtual void setParsingMSInlineAsm(bool V) = 0; virtual bool isParsingMSInlineAsm() = 0; + virtual bool discardLTOSymbol(StringRef) const { return false; } + virtual bool isParsingMasm() const { return false; } virtual bool defineMacro(StringRef Name, StringRef Value) { return true; } diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "llvm/LTO/LTO.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/StackSafetyAnalysis.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -752,6 +754,7 @@ Skip(); std::set NonPrevailingComdats; + SmallSet NonPrevailingAsmSymbols; for (const InputFile::Symbol &Sym : Syms) { assert(ResI != ResE); SymbolResolution Res = *ResI++; @@ -798,7 +801,14 @@ GV->setDLLStorageClass(GlobalValue::DLLStorageClassTypes:: DefaultStorageClass); } + } else if (auto *AS = Msym.dyn_cast()) { + // Collect non-prevailing symbols. + if (!Res.Prevailing) + NonPrevailingAsmSymbols.insert(AS->first); + } else { + llvm_unreachable("unknown symbol type"); } + // Common resolution: collect the maximum size/alignment over all commons. // We also record if we see an instance of a common as prevailing, so that // if none is prevailing we can ignore it later. @@ -812,11 +822,29 @@ CommonRes.Align = max(*SymAlign, CommonRes.Align); CommonRes.Prevailing |= Res.Prevailing; } - } + if (!M.getComdatSymbolTable().empty()) for (GlobalValue &GV : M.global_values()) handleNonPrevailingComdat(GV, NonPrevailingComdats); + + // Prepend ".lto_discard , *" directive to each module inline asm + // block. + if (!M.getModuleInlineAsm().empty()) { + std::string NewIA = ".lto_discard"; + if (!NonPrevailingAsmSymbols.empty()) { + // Don't dicard a symbol if there is a live .symver for it. + ModuleSymbolTable::CollectAsmSymvers( + M, [&](StringRef Name, StringRef Alias) { + if (!NonPrevailingAsmSymbols.count(Alias)) + NonPrevailingAsmSymbols.erase(Name); + }); + NewIA += " " + llvm::join(NonPrevailingAsmSymbols, ", "); + } + NewIA += "\n"; + M.setModuleInlineAsm(NewIA + M.getModuleInlineAsm()); + } + assert(MsymI == MsymE); return std::move(Mod); } diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -54,6 +55,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SMLoc.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" #include @@ -168,6 +170,9 @@ /// List of forward directional labels for diagnosis at the end. SmallVector, 4> DirLabels; + bool EnableLTODiscardSymbols = false; + SmallSet LTODiscardSymbols; + /// AssemblerDialect. ~OU means unset value and use value provided by MAI. unsigned AssemblerDialect = ~0U; @@ -235,6 +240,12 @@ } bool isParsingMSInlineAsm() override { return ParsingMSInlineAsm; } + bool discardLTOSymbol(StringRef Name) const override { + if (!LTODiscardSymbols.empty()) + assert(EnableLTODiscardSymbols); + return LTODiscardSymbols.contains(Name); + } + bool parseMSInlineAsm(void *AsmLoc, std::string &AsmString, unsigned &NumOutputs, unsigned &NumInputs, SmallVectorImpl> &OpDecls, @@ -516,6 +527,7 @@ DK_ADDRSIG, DK_ADDRSIG_SYM, DK_PSEUDO_PROBE, + DK_LTO_DISCARD, DK_END }; @@ -682,6 +694,9 @@ // .pseudoprobe bool parseDirectivePseudoProbe(); + // ".lto_discard" + bool parseDirectiveLTODiscard(); + // Directives to support address-significance tables. bool parseDirectiveAddrsig(); bool parseDirectiveAddrsigSym(); @@ -892,6 +907,11 @@ } bool AsmParser::Run(bool NoInitialTextSection, bool NoFinalize) { + // Only enable EnableLTODiscardSymbols for inline assembly. + SaveAndRestore SaveLTODiscard(EnableLTODiscardSymbols, + Ctx.getInlineSourceManager() != nullptr); + LTODiscardSymbols.clear(); + // Create the initial section, if requested. if (!NoInitialTextSection) Out.InitSections(false); @@ -1770,7 +1790,6 @@ StringMap::const_iterator DirKindIt = DirectiveKindMap.find(IDVal.lower()); DirectiveKind DirKind = (DirKindIt == DirectiveKindMap.end()) - ? DK_NO_DIRECTIVE : DirKindIt->getValue(); switch (DirKind) { @@ -1868,6 +1887,9 @@ Lex(); } + if (discardLTOSymbol(IDVal)) + return false; + getTargetParser().doBeforeLabelEmit(Sym); // Emit the label. @@ -2208,6 +2230,8 @@ return parseDirectiveAddrsigSym(); case DK_PSEUDO_PROBE: return parseDirectivePseudoProbe(); + case DK_LTO_DISCARD: + return parseDirectiveLTODiscard(); } return Error(IDLoc, "unknown directive"); @@ -2852,6 +2876,9 @@ return false; } + if (discardLTOSymbol(Name)) + return false; + // Do the assignment. Out.emitAssignment(Sym, Value); if (NoDeadStrip) @@ -4870,6 +4897,10 @@ SMLoc Loc = getTok().getLoc(); if (parseIdentifier(Name)) return Error(Loc, "expected identifier"); + + if (discardLTOSymbol(Name)) + return false; + MCSymbol *Sym = getContext().getOrCreateSymbol(Name); // Assembler local symbols don't make any sense here. Complain loudly. @@ -5493,6 +5524,7 @@ DirectiveKindMap[".addrsig"] = DK_ADDRSIG; DirectiveKindMap[".addrsig_sym"] = DK_ADDRSIG_SYM; DirectiveKindMap[".pseudoprobe"] = DK_PSEUDO_PROBE; + DirectiveKindMap[".lto_discard"] = DK_LTO_DISCARD; } MCAsmMacro *AsmParser::parseMacroLikeBody(SMLoc DirectiveLoc) { @@ -5806,6 +5838,27 @@ return false; } +/// parseDirectiveLTODiscard +/// ::= ".lto_discard" [ identifier ( , identifier )* ] +/// This directive bears two meanings: a module inlineasm start marker and +/// gives a list of symbols that are to be discarded. +bool AsmParser::parseDirectiveLTODiscard() { + auto ParseOp = [&]() -> bool { + StringRef Name; + SMLoc Loc = getTok().getLoc(); + if (parseIdentifier(Name)) + return Error(Loc, "expected identifier"); + if (EnableLTODiscardSymbols) + LTODiscardSymbols.insert(Name); + return false; + }; + + LTODiscardSymbols.clear(); + if (parseMany(ParseOp)) + return addErrorSuffix(" in directive"); + return false; +} + // We are comparing pointers, but the pointers are relative to a single string. // Thus, this should always be deterministic. static int rewritesSort(const AsmRewrite *AsmRewriteA, diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp --- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp @@ -182,6 +182,12 @@ if (getParser().parseIdentifier(Name)) return TokError("expected identifier in directive"); + if (getParser().discardLTOSymbol(Name)) { + if (getLexer().is(AsmToken::EndOfStatement)) + break; + continue; + } + MCSymbol *Sym = getContext().getOrCreateSymbol(Name); getStreamer().emitSymbolAttribute(Sym, Attr); diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp --- a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -328,13 +328,15 @@ std::pair ModuleSanitizerCoverage::CreateSecStartEnd(Module &M, const char *Section, Type *Ty) { + // Use ExternalWeak so that if all sections are discarded due to section + // garbage collection, the linker will not report undefined symbol errors. GlobalVariable *SecStart = new GlobalVariable( - M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, - nullptr, getSectionStart(Section)); + M, Ty->getPointerElementType(), false, + GlobalVariable::ExternalWeakLinkage, nullptr, getSectionStart(Section)); SecStart->setVisibility(GlobalValue::HiddenVisibility); GlobalVariable *SecEnd = new GlobalVariable( - M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, - nullptr, getSectionEnd(Section)); + M, Ty->getPointerElementType(), false, + GlobalVariable::ExternalWeakLinkage, nullptr, getSectionEnd(Section)); SecEnd->setVisibility(GlobalValue::HiddenVisibility); IRBuilder<> IRB(M.getContext()); if (!TargetTriple.isOSBinFormatCOFF()) diff --git a/llvm/test/Instrumentation/SanitizerCoverage/inline-8bit-counters.ll b/llvm/test/Instrumentation/SanitizerCoverage/inline-8bit-counters.ll --- a/llvm/test/Instrumentation/SanitizerCoverage/inline-8bit-counters.ll +++ b/llvm/test/Instrumentation/SanitizerCoverage/inline-8bit-counters.ll @@ -2,11 +2,14 @@ ; RUN: opt < %s -sancov -sanitizer-coverage-level=1 -sanitizer-coverage-inline-8bit-counters=1 -S -enable-new-pm=0 | FileCheck %s ; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-inline-8bit-counters=1 -S | FileCheck %s +; CHECK: @__sancov_gen_ = private global [1 x i8] zeroinitializer, section "__sancov_cntrs", comdat($foo), align 1 +; CHECK: @__start___sancov_cntrs = extern_weak hidden global i8 +; CHECK-NEXT: @__stop___sancov_cntrs = extern_weak hidden global i8 + target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-unknown-linux-gnu" define void @foo() { entry: -; CHECK: section "__sancov_cntrs", comdat($foo), align 1 ; CHECK: %0 = load i8, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @__sancov_gen_, i64 0, i64 0), align 1, !nosanitize ; CHECK: %1 = add i8 %0, 1 ; CHECK: store i8 %1, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @__sancov_gen_, i64 0, i64 0), align 1, !nosanitize diff --git a/llvm/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll b/llvm/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll --- a/llvm/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll +++ b/llvm/test/Instrumentation/SanitizerCoverage/inline-bool-flag.ll @@ -4,6 +4,8 @@ ; CHECK: $foo = comdat noduplicates ; CHECK: @__sancov_gen_ = private global [1 x i1] zeroinitializer, section "__sancov_bools", comdat($foo), align 1{{$}} +; CHECK: @__start___sancov_bools = extern_weak hidden global i1 +; CHECK-NEXT: @__stop___sancov_bools = extern_weak hidden global i1 ; CHECK-NOT: @llvm.used = ; CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* bitcast ([1 x i1]* @__sancov_gen_ to i8*)], section "llvm.metadata" diff --git a/llvm/test/Instrumentation/SanitizerCoverage/pc-table.ll b/llvm/test/Instrumentation/SanitizerCoverage/pc-table.ll --- a/llvm/test/Instrumentation/SanitizerCoverage/pc-table.ll +++ b/llvm/test/Instrumentation/SanitizerCoverage/pc-table.ll @@ -6,6 +6,7 @@ ; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=3 -sanitizer-coverage-inline-8bit-counters -sanitizer-coverage-pc-table=1 -S | FileCheck %s ; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=3 -sanitizer-coverage-inline-bool-flag -sanitizer-coverage-pc-table=1 -S | FileCheck %s + target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-unknown-linux-gnu" define void @foo(i32* %a) sanitize_address { @@ -22,6 +23,8 @@ } ; CHECK: private constant [6 x i64*] [{{.*}}@foo{{.*}}blockaddress{{.*}}blockaddress{{.*}}], section "__sancov_pcs", comdat($foo), align 8 +; CHECK: @__start___sancov_pcs = extern_weak hidden global i64 +; CHECK-NEXT: @__stop___sancov_pcs = extern_weak hidden global i64 ; CHECK: define internal void @sancov.module_ctor ; CHECK: call void @__sanitizer_cov ; CHECK: call void @__sanitizer_cov_pcs_init