diff --git a/llvm/include/llvm/MC/MCRegisterInfo.h b/llvm/include/llvm/MC/MCRegisterInfo.h --- a/llvm/include/llvm/MC/MCRegisterInfo.h +++ b/llvm/include/llvm/MC/MCRegisterInfo.h @@ -167,14 +167,18 @@ const uint16_t *RegEncodingTable; // Pointer to array of register // encodings. - unsigned L2DwarfRegsSize; - unsigned EHL2DwarfRegsSize; - unsigned Dwarf2LRegsSize; - unsigned EHDwarf2LRegsSize; - const DwarfLLVMRegPair *L2DwarfRegs; // LLVM to Dwarf regs mapping - const DwarfLLVMRegPair *EHL2DwarfRegs; // LLVM to Dwarf regs mapping EH - const DwarfLLVMRegPair *Dwarf2LRegs; // Dwarf to LLVM regs mapping - const DwarfLLVMRegPair *EHDwarf2LRegs; // Dwarf to LLVM regs mapping EH + unsigned L2DwarfRegsSize = 0; + unsigned EHL2DwarfRegsSize = 0; + unsigned Dwarf2LRegsSize = 0; + unsigned EHDwarf2LRegsSize = 0; + // LLVM to Dwarf regs mapping + const DwarfLLVMRegPair *L2DwarfRegs = nullptr; + // LLVM to Dwarf regs mapping EH + const DwarfLLVMRegPair *EHL2DwarfRegs = nullptr; + // Dwarf to LLVM regs mapping + const DwarfLLVMRegPair *Dwarf2LRegs = nullptr; + // Dwarf to LLVM regs mapping EH + const DwarfLLVMRegPair *EHDwarf2LRegs = nullptr; DenseMap L2SEHRegs; // LLVM to SEH regs mapping DenseMap L2CVRegs; // LLVM to CV regs mapping 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 @@ -30,12 +30,16 @@ #include "llvm/LTO/LTOBackend.h" #include "llvm/LTO/SummaryBasedOptimizations.h" #include "llvm/Linker/IRMover.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" @@ -700,6 +704,58 @@ GO->setComdat(nullptr); } +static const char *getTargetAsmSeparator(const Module &M) { + std::string Err; + const Triple TT(M.getTargetTriple()); + const Target *T = TargetRegistry::lookupTarget(TT.str(), Err); + assert(T && T->hasMCAsmParser()); + + MCTargetOptions MCOptions; + MCRegisterInfo MRI; + std::unique_ptr MAI(T->createMCAsmInfo(MRI, TT.str(), MCOptions)); + if (!MAI) + return "\\n"; + return MAI->getSeparatorString(); +} + +static void +handleModuleInlineAsm(Module &M, std::set &NonPrevailingAsmSymbols) { + ModuleSymbolTable::CollectAsmSymvers(M, [&](StringRef Name, StringRef Alias) { + if (!NonPrevailingAsmSymbols.count(Alias)) + NonPrevailingAsmSymbols.erase(Name); + }); + + if (NonPrevailingAsmSymbols.empty()) + return; + + std::string Sep = getTargetAsmSeparator(M); + std::string PrefixPat = "(^|[[:blank:]]+)"; + std::string SuffixPat = "($|" + Sep + ")"; + std::string Symbols = "(" + llvm::join(NonPrevailingAsmSymbols, "|") + ")"; + std::vector Pats = {// Remove assignment directive. + "\\.(set|equ|equiv|eqv)[[:blank:]]+" + + Symbols + "[[:blank:]]*,[^" + Sep + + "]+" + SuffixPat, + // Remove labels. + Symbols + ":", + // Remove symbols. + "\\.(weak|global|globl)[[:blank:]]+" + + Symbols + "[[:blank:]]*" + SuffixPat}; + + std::string IA = M.getModuleInlineAsm(); + for (bool Changed = true; Changed;) { + std::string PrevIA = IA; + for (auto &P : Pats) { + Regex RE(PrefixPat + P, Regex::Newline); + assert(RE.isValid()); + IA = RE.sub("", IA); + } + Changed = IA != PrevIA; + } + + M.setModuleInlineAsm(IA); +} + // Add a regular LTO object to the link. // The resulting module needs to be linked into the combined LTO module with // linkRegularLTO. @@ -752,6 +808,7 @@ Skip(); std::set NonPrevailingComdats; + std::set NonPrevailingAsmSymbol; for (const InputFile::Symbol &Sym : Syms) { assert(ResI != ResE); SymbolResolution Res = *ResI++; @@ -798,7 +855,13 @@ GV->setDLLStorageClass(GlobalValue::DLLStorageClassTypes:: DefaultStorageClass); } - } + } else if (auto *AS = Msym.dyn_cast()) { + // Collect non-prevailing symbols. + if (!Res.Prevailing) + NonPrevailingAsmSymbol.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 +875,15 @@ CommonRes.Align = max(*SymAlign, CommonRes.Align); CommonRes.Prevailing |= Res.Prevailing; } - } + if (!M.getComdatSymbolTable().empty()) for (GlobalValue &GV : M.global_values()) handleNonPrevailingComdat(GV, NonPrevailingComdats); + + if (!NonPrevailingAsmSymbol.empty()) + handleModuleInlineAsm(M, NonPrevailingAsmSymbol); + assert(MsymI == MsymE); return std::move(Mod); } diff --git a/llvm/test/LTO/X86/weak-asm.ll b/llvm/test/LTO/X86/weak-asm.ll new file mode 100644 --- /dev/null +++ b/llvm/test/LTO/X86/weak-asm.ll @@ -0,0 +1,60 @@ +; Check that non-prevailing symbols in module inline assembly are discarded +; during regular LTO otherwise the final symbol binding could be wrong. + +; RUN: split-file %s %t +; RUN: opt %t/t1.ll -o %t1 +; RUN: opt %t/t2.ll -o %t2 +; RUN: opt %t/t3.ll -o %t3 + +; RUN: llvm-lto2 run -o %to1 -save-temps %t1 %t2 \ +; RUN: -r %t1,foo,px \ +; RUN: -r %t2,foo, \ +; RUN: -r %t2,bar,pl +; RUN: llvm-dis < %to1.0.0.preopt.bc -o - | FileCheck %s --check-prefix=ASM +; RUN: llvm-nm %to1.0 | FileCheck %s --check-prefix=SYM +; RUN: llvm-objdump -d --disassemble-symbols=foo %to1.0 \ +; RUN: | FileCheck %s --check-prefix=DEF + +; RUN: llvm-lto2 run -o %to2 -save-temps %t2 %t3 \ +; RUN: -r %t2,foo, \ +; RUN: -r %t2,bar,pl \ +; RUN: -r %t3,foo,px +; RUN: llvm-dis < %to2.0.0.preopt.bc -o - | FileCheck %s --check-prefix=ASM +; RUN: llvm-nm %to2.0 | FileCheck %s --check-prefix=SYM +; RUN: llvm-objdump -d --disassemble-symbols=foo %to2.0 \ +; RUN: | FileCheck %s --check-prefix=DEF + +; ASM: module asm "{{[[:blank:]]*}}" + +; SYM: T foo + +; DEF: leal 2(%rdi), %eax + +;--- t1.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define dso_local i32 @foo(i32 %0) { + %2 = add nsw i32 %0, 2 + ret i32 %2 +} + +;--- t2.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +module asm ".weak foo" +module asm "\09 .equ foo,bar" + +@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (i32 (i32)* @bar to i8*)], section "llvm.metadata" + +define internal i32 @bar(i32 %0) { + %2 = add nsw i32 %0, 1 + ret i32 %2 +} + +;--- t3.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +module asm " .global foo ; foo: leal 2(%rdi), %eax"