diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp index 9aec11e69fde1..44a6894d30fb2 100644 --- a/clang/lib/Driver/ToolChains/WebAssembly.cpp +++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp @@ -163,6 +163,8 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA, AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args); } + ToolChain.addProfileRTLibs(Args, CmdArgs); + CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake index 809e927715691..d00d39518104b 100644 --- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake +++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake @@ -77,7 +77,7 @@ set(ALL_HWASAN_SUPPORTED_ARCH ${X86_64} ${ARM64} ${RISCV64}) set(ALL_MEMPROF_SUPPORTED_ARCH ${X86_64}) set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC32} ${PPC64} ${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON} - ${RISCV32} ${RISCV64} ${LOONGARCH64}) + ${RISCV32} ${RISCV64} ${LOONGARCH64} ${WASM32}) set(ALL_CTX_PROFILE_SUPPORTED_ARCH ${X86_64}) if (OS_NAME MATCHES "FreeBSD") set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64}) diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index a93a88a920500..a494e0532a50b 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -822,7 +822,7 @@ else() endif() if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX|WASI") set(COMPILER_RT_HAS_PROFILE TRUE) else() set(COMPILER_RT_HAS_PROFILE FALSE) diff --git a/compiler-rt/lib/profile/CMakeLists.txt b/compiler-rt/lib/profile/CMakeLists.txt index ef23492514898..a6402f80b890a 100644 --- a/compiler-rt/lib/profile/CMakeLists.txt +++ b/compiler-rt/lib/profile/CMakeLists.txt @@ -38,6 +38,17 @@ int main() { " COMPILER_RT_TARGET_HAS_FCNTL_LCK) +CHECK_CXX_SOURCE_COMPILES(" +#include + +int fd; +int main() { + flock(fd, LOCK_EX); + return 0; +} + +" COMPILER_RT_TARGET_HAS_FLOCK) + CHECK_CXX_SOURCE_COMPILES(" #include int main() { @@ -93,6 +104,13 @@ if(FUCHSIA OR UNIX) -Wno-pedantic) endif() +if(CMAKE_SYSTEM_NAME STREQUAL "WASI") + set(EXTRA_FLAGS + ${EXTRA_FLAGS} + -D_WASI_EMULATED_MMAN + -D_WASI_EMULATED_GETPID) +endif() + if(COMPILER_RT_TARGET_HAS_ATOMICS) set(EXTRA_FLAGS ${EXTRA_FLAGS} @@ -105,6 +123,12 @@ if(COMPILER_RT_TARGET_HAS_FCNTL_LCK) -DCOMPILER_RT_HAS_FCNTL_LCK=1) endif() +if(COMPILER_RT_TARGET_HAS_FLOCK) + set(EXTRA_FLAGS + ${EXTRA_FLAGS} + -DCOMPILER_RT_HAS_FLOCK=1) +endif() + if(COMPILER_RT_TARGET_HAS_UNAME) set(EXTRA_FLAGS ${EXTRA_FLAGS} diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c index d6e2175169e4a..f67d95d21a7b5 100644 --- a/compiler-rt/lib/profile/GCDAProfiling.c +++ b/compiler-rt/lib/profile/GCDAProfiling.c @@ -584,7 +584,7 @@ void llvm_reset_counters(void) { } } -#if !defined(_WIN32) +#if !defined(_WIN32) && !defined(__wasm__) COMPILER_RT_VISIBILITY pid_t __gcov_fork() { pid_t parent_pid = getpid(); diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c index b766436497b74..02f23379ce98b 100644 --- a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c +++ b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c @@ -6,11 +6,11 @@ |* \*===----------------------------------------------------------------------===*/ -#if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \ - (defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__) || \ - defined(_AIX) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \ + (defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__) || \ + defined(_AIX) || defined(__wasm__) -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__wasm__) #include #include #endif diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformOther.c b/compiler-rt/lib/profile/InstrProfilingPlatformOther.c index aa79a5641ceca..52e82273f8aad 100644 --- a/compiler-rt/lib/profile/InstrProfilingPlatformOther.c +++ b/compiler-rt/lib/profile/InstrProfilingPlatformOther.c @@ -8,7 +8,8 @@ #if !defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__) && \ !defined(__Fuchsia__) && !(defined(__sun__) && defined(__svr4__)) && \ - !defined(__NetBSD__) && !defined(_WIN32) && !defined(_AIX) + !defined(__NetBSD__) && !defined(_WIN32) && !defined(_AIX) && \ + !defined(__wasm__) #include #include diff --git a/compiler-rt/lib/profile/InstrProfilingPort.h b/compiler-rt/lib/profile/InstrProfilingPort.h index ed0905cc5f202..8715a3b0d2a6f 100644 --- a/compiler-rt/lib/profile/InstrProfilingPort.h +++ b/compiler-rt/lib/profile/InstrProfilingPort.h @@ -54,7 +54,7 @@ #endif #define COMPILER_RT_MAX_HOSTLEN 128 -#ifdef __ORBIS__ +#if defined(__ORBIS__) || defined(__wasi__) #define COMPILER_RT_GETHOSTNAME(Name, Len) ((void)(Name), (void)(Len), (-1)) #else #define COMPILER_RT_GETHOSTNAME(Name, Len) lprofGetHostName(Name, Len) diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.c b/compiler-rt/lib/profile/InstrProfilingUtil.c index 642393d432d7e..95ec4080ba250 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.c +++ b/compiler-rt/lib/profile/InstrProfilingUtil.c @@ -152,9 +152,11 @@ COMPILER_RT_VISIBILITY int lprofLockFd(int fd) { } } return 0; -#else +#elif defined(COMPILER_RT_HAS_FLOCK) flock(fd, LOCK_EX); return 0; +#else + return 0; #endif } @@ -177,9 +179,11 @@ COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) { } } return 0; -#else +#elif defined(COMPILER_RT_HAS_FLOCK) flock(fd, LOCK_UN); return 0; +#else + return 0; #endif } @@ -353,8 +357,8 @@ COMPILER_RT_VISIBILITY void lprofRestoreSigKill(void) { COMPILER_RT_VISIBILITY int lprofReleaseMemoryPagesToOS(uintptr_t Begin, uintptr_t End) { -#if defined(__ve__) - // VE doesn't support madvise. +#if defined(__ve__) || defined(__wasi__) + // VE and WASI doesn't support madvise. return 0; #else size_t PageSize = getpagesize(); diff --git a/lld/test/wasm/custom-section-align.s b/lld/test/wasm/custom-section-align.s new file mode 100644 index 0000000000000..0e46177f4cdb7 --- /dev/null +++ b/lld/test/wasm/custom-section-align.s @@ -0,0 +1,31 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld --no-entry %t.o -o %t.wasm +# RUN: obj2yaml %t.wasm | FileCheck %s + +# Check that "__llvm_covfun" custom section is aligned to 8 bytes. + + .section .custom_section.__llvm_covfun,"GR",@,__covrec_A + .int32 1 + .int8 2 +# pad .int8 0 +# .int8 0 +# .int8 0 + + .section .custom_section.__llvm_covfun,"GR",@,__covrec_B + .int32 3 + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: __llvm_covfun +# CHECK-NEXT: Payload: '010000000200000003000000' + +# Check that regular custom sections are not aligned. + .section .custom_section.foo,"GR",@,foo_A + .int32 1 + .int8 2 + + .section .custom_section.foo,"GR",@,foo_B + .int32 3 + +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: foo +# CHECK-NEXT: Payload: '010000000203000000' diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h index 14eb008c212fb..d6769bcf5c823 100644 --- a/lld/wasm/InputChunks.h +++ b/lld/wasm/InputChunks.h @@ -177,8 +177,9 @@ class MergeInputChunk : public InputChunk { inputSectionOffset = seg.SectionOffset; } - MergeInputChunk(const WasmSection &s, ObjFile *f) - : InputChunk(f, Merge, s.Name, 0, llvm::wasm::WASM_SEG_FLAG_STRINGS) { + MergeInputChunk(const WasmSection &s, ObjFile *f, uint32_t alignment) + : InputChunk(f, Merge, s.Name, alignment, + llvm::wasm::WASM_SEG_FLAG_STRINGS) { assert(s.Type == llvm::wasm::WASM_SEC_CUSTOM); comdat = s.Comdat; rawData = s.Content; @@ -234,6 +235,7 @@ class SyntheticMergedChunk : public InputChunk { void addMergeChunk(MergeInputChunk *ms) { comdat = ms->getComdat(); + alignment = std::max(alignment, ms->alignment); ms->parent = this; chunks.push_back(ms); } @@ -337,8 +339,8 @@ class SyntheticFunction : public InputFunction { // Represents a single Wasm Section within an input file. class InputSection : public InputChunk { public: - InputSection(const WasmSection &s, ObjFile *f) - : InputChunk(f, InputChunk::Section, s.Name), + InputSection(const WasmSection &s, ObjFile *f, uint32_t alignment) + : InputChunk(f, InputChunk::Section, s.Name, alignment), tombstoneValue(getTombstoneForSection(s.Name)), section(s) { assert(section.Type == llvm::wasm::WASM_SEC_CUSTOM); comdat = section.Comdat; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index de8e707ab2b49..420865e2aea8e 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -18,6 +18,7 @@ #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Wasm.h" +#include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -451,6 +452,18 @@ void SharedFile::parse() { } } +// Returns the alignment for a custom section. This is used to concatenate +// custom sections with the same name into a single custom section. +static uint32_t getCustomSectionAlignment(const WasmSection &sec) { + // TODO: Add a section attribute for alignment in the linking spec. + if (sec.Name == getInstrProfSectionName(IPSK_covfun, Triple::Wasm) || + sec.Name == getInstrProfSectionName(IPSK_covmap, Triple::Wasm)) { + // llvm-cov assumes that coverage metadata sections are 8-byte aligned. + return 8; + } + return 1; +} + WasmFileBase::WasmFileBase(Kind k, MemoryBufferRef m) : InputFile(k, m) { // Parse a memory buffer as a wasm file. LLVM_DEBUG(dbgs() << "Reading object: " << toString(this) << "\n"); @@ -520,10 +533,11 @@ void ObjFile::parse(bool ignoreComdats) { dataSection = §ion; } else if (section.Type == WASM_SEC_CUSTOM) { InputChunk *customSec; + uint32_t alignment = getCustomSectionAlignment(section); if (shouldMerge(section)) - customSec = make(section, this); + customSec = make(section, this, alignment); else - customSec = make(section, this); + customSec = make(section, this, alignment); customSec->discarded = isExcludedByComdat(customSec); customSections.emplace_back(customSec); customSections.back()->setRelocations(section.Relocations); diff --git a/lld/wasm/OutputSections.cpp b/lld/wasm/OutputSections.cpp index b0b2446cb56bf..e4f75829ec4c3 100644 --- a/lld/wasm/OutputSections.cpp +++ b/lld/wasm/OutputSections.cpp @@ -244,6 +244,7 @@ void CustomSection::finalizeContents() { for (InputChunk *section : inputSections) { assert(!section->discarded); + payloadSize = alignTo(payloadSize, section->alignment); section->outSecOff = payloadSize; payloadSize += section->getSize(); } diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h index f05b90114d75a..886b4d3d6894d 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h @@ -180,6 +180,7 @@ class BinaryCoverageReader : public CoverageMappingReader { }; using FuncRecordsStorage = std::unique_ptr; + using CoverageMapCopyStorage = std::unique_ptr; private: std::vector Filenames; @@ -195,9 +196,16 @@ class BinaryCoverageReader : public CoverageMappingReader { // D69471, which can split up function records into multiple sections on ELF. FuncRecordsStorage FuncRecords; + // Used to tie the lifetimes of an optional copy of the coverage mapping data + // to the lifetime of this BinaryCoverageReader instance. Needed to support + // Wasm object format, which might require realignment of section contents. + CoverageMapCopyStorage CoverageMapCopy; + BinaryCoverageReader(std::unique_ptr Symtab, - FuncRecordsStorage &&FuncRecords) - : ProfileNames(std::move(Symtab)), FuncRecords(std::move(FuncRecords)) {} + FuncRecordsStorage &&FuncRecords, + CoverageMapCopyStorage &&CoverageMapCopy) + : ProfileNames(std::move(Symtab)), FuncRecords(std::move(FuncRecords)), + CoverageMapCopy(std::move(CoverageMapCopy)) {} public: BinaryCoverageReader(const BinaryCoverageReader &) = delete; @@ -212,6 +220,7 @@ class BinaryCoverageReader : public CoverageMappingReader { static Expected> createCoverageReaderFromBuffer( StringRef Coverage, FuncRecordsStorage &&FuncRecords, + CoverageMapCopyStorage &&CoverageMap, std::unique_ptr ProfileNamesPtr, uint8_t BytesInAddress, llvm::endianness Endian, StringRef CompilationDir = ""); diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index 0d3e4ba5662e0..ce50a3c19ffe0 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2171,7 +2171,11 @@ MCSection *TargetLoweringObjectFileWasm::getExplicitSectionGlobal( // This could be avoided if all data segements (the wasm sense) were // represented as their own sections (in the llvm sense). // TODO(sbc): https://github.com/WebAssembly/tool-conventions/issues/138 - if (Name == ".llvmcmd" || Name == ".llvmbc") + if (Name == getInstrProfSectionName(IPSK_covmap, Triple::Wasm, + /*AddSegmentInfo=*/false) || + Name == getInstrProfSectionName(IPSK_covfun, Triple::Wasm, + /*AddSegmentInfo=*/false) || + Name == ".llvmbc" || Name == ".llvmcmd") Kind = SectionKind::getMetadata(); StringRef Group = ""; diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp index ac3946b6ef46f..b97f9d9f5fed0 100644 --- a/llvm/lib/MC/MCContext.cpp +++ b/llvm/lib/MC/MCContext.cpp @@ -757,6 +757,11 @@ MCSectionWasm *MCContext::getWasmSection(const Twine &Section, SectionKind K, if (!Group.isTriviallyEmpty() && !Group.str().empty()) { GroupSym = cast(getOrCreateSymbol(Group)); GroupSym->setComdat(true); + if (K.isMetadata() && !GroupSym->getType().has_value()) { + // Comdat group symbol associated with a custom section is a section + // symbol (not a data symbol). + GroupSym->setType(wasm::WASM_SYMBOL_TYPE_SECTION); + } } return getWasmSection(Section, K, Flags, GroupSym, UniqueID); diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp index bc4e780fb67a6..461fc43d32f8d 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -18,12 +18,14 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Wasm.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Object/Error.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/Wasm.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compression.h" @@ -894,13 +896,15 @@ static Error readCoverageMappingData( Expected> BinaryCoverageReader::createCoverageReaderFromBuffer( StringRef Coverage, FuncRecordsStorage &&FuncRecords, + CoverageMapCopyStorage &&CoverageMap, std::unique_ptr ProfileNamesPtr, uint8_t BytesInAddress, llvm::endianness Endian, StringRef CompilationDir) { if (ProfileNamesPtr == nullptr) return make_error(coveragemap_error::malformed, "Caller must provide ProfileNames"); - std::unique_ptr Reader(new BinaryCoverageReader( - std::move(ProfileNamesPtr), std::move(FuncRecords))); + std::unique_ptr Reader( + new BinaryCoverageReader(std::move(ProfileNamesPtr), + std::move(FuncRecords), std::move(CoverageMap))); InstrProfSymtab &ProfileNames = *Reader->ProfileNames; StringRef FuncRecordsRef = Reader->FuncRecords->getBuffer(); if (BytesInAddress == 4 && Endian == llvm::endianness::little) { @@ -1035,8 +1039,8 @@ loadTestingFormat(StringRef Data, StringRef CompilationDir) { MemoryBuffer::getMemBuffer(Data); return BinaryCoverageReader::createCoverageReaderFromBuffer( - CoverageMapping, std::move(CoverageRecords), std::move(ProfileNames), - BytesInAddress, Endian, CompilationDir); + CoverageMapping, std::move(CoverageRecords), nullptr, + std::move(ProfileNames), BytesInAddress, Endian, CompilationDir); } /// Find all sections that match \p IPSK name. There may be more than one if @@ -1075,6 +1079,53 @@ lookupSections(ObjectFile &OF, InstrProfSectKind IPSK) { return Sections; } +/// Find a section that matches \p Name and is allocatable at runtime. +/// +/// Returns the contents of the section and its start offset in the object file. +static Expected> +lookupAllocatableSection(ObjectFile &OF, InstrProfSectKind IPSK) { + // On Wasm, allocatable sections can live only in data segments. + if (auto *WOF = dyn_cast(&OF)) { + std::vector Segments; + auto ObjFormat = OF.getTripleObjectFormat(); + auto Name = + getInstrProfSectionName(IPSK, ObjFormat, /*AddSegmentInfo=*/false); + for (const auto &DebugName : WOF->debugNames()) { + if (DebugName.Type != wasm::NameType::DATA_SEGMENT || + DebugName.Name != Name) + continue; + if (DebugName.Index >= WOF->dataSegments().size()) + return make_error(coveragemap_error::malformed); + auto &Segment = WOF->dataSegments()[DebugName.Index]; + Segments.push_back(&Segment); + } + if (Segments.empty()) + return make_error(coveragemap_error::no_data_found); + if (Segments.size() != 1) + return make_error(coveragemap_error::malformed); + + const auto &Segment = *Segments.front(); + auto &Data = Segment.Data; + StringRef Content(reinterpret_cast(Data.Content.data()), + Data.Content.size()); + return std::make_pair(Content, Segment.SectionOffset); + } + + // On other object file types, delegate to lookupSections to find the section. + auto Sections = lookupSections(OF, IPSK); + if (!Sections) + return Sections.takeError(); + if (Sections->size() != 1) + return make_error( + coveragemap_error::malformed, + "the size of coverage mapping section is not one"); + auto &Section = Sections->front(); + auto ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + return std::make_pair(*ContentsOrErr, Section.getAddress()); +} + static Expected> loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, StringRef CompilationDir = "", @@ -1105,23 +1156,20 @@ loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, // Look for the sections that we are interested in. auto ProfileNames = std::make_unique(); - std::vector NamesSectionRefs; // If IPSK_name is not found, fallback to search for IPK_covname, which is // used when binary correlation is enabled. - auto NamesSection = lookupSections(*OF, IPSK_name); + auto NamesSection = lookupAllocatableSection(*OF, IPSK_name); if (auto E = NamesSection.takeError()) { consumeError(std::move(E)); - NamesSection = lookupSections(*OF, IPSK_covname); + NamesSection = lookupAllocatableSection(*OF, IPSK_covname); if (auto E = NamesSection.takeError()) return std::move(E); } - NamesSectionRefs = *NamesSection; - if (NamesSectionRefs.size() != 1) - return make_error( - coveragemap_error::malformed, - "the size of coverage mapping section is not one"); - if (Error E = ProfileNames->create(NamesSectionRefs.back())) + uint64_t NamesAddress; + StringRef NamesContent; + std::tie(NamesContent, NamesAddress) = *NamesSection; + if (Error E = ProfileNames->create(NamesContent, NamesAddress)) return std::move(E); auto CoverageSection = lookupSections(*OF, IPSK_covmap); @@ -1136,6 +1184,15 @@ loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, return CoverageMappingOrErr.takeError(); StringRef CoverageMapping = CoverageMappingOrErr.get(); + // If the coverage mapping section is not aligned to 8 bytes, copy it to a + // new buffer that is. Wasm format typically has unaligned section contents + // because it doesn't have a good way to insert padding bytes. + std::unique_ptr CoverageMapCopy; + if (!isAddrAligned(Align(8), CoverageMapping.data())) { + CoverageMapCopy = MemoryBuffer::getMemBufferCopy(CoverageMapping); + CoverageMapping = CoverageMapCopy->getBuffer(); + } + // Look for the coverage records section (Version4 only). auto CoverageRecordsSections = lookupSections(*OF, IPSK_covfun); @@ -1184,8 +1241,8 @@ loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, *BinaryID = getBuildID(OF.get()); return BinaryCoverageReader::createCoverageReaderFromBuffer( - CoverageMapping, std::move(FuncRecords), std::move(ProfileNames), - BytesInAddress, Endian, CompilationDir); + CoverageMapping, std::move(FuncRecords), std::move(CoverageMapCopy), + std::move(ProfileNames), BytesInAddress, Endian, CompilationDir); } /// Determine whether \p Arch is invalid or empty, given \p Bin. diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp index 1c95a4606ecc5..929c787442057 100644 --- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -1406,9 +1406,10 @@ static inline Constant *getFuncAddrForProfData(Function *Fn) { static bool needsRuntimeRegistrationOfSectionRange(const Triple &TT) { // compiler-rt uses linker support to get data/counters/name start/end for - // ELF, COFF, Mach-O and XCOFF. + // ELF, COFF, Mach-O, XCOFF, and Wasm. if (TT.isOSBinFormatELF() || TT.isOSBinFormatCOFF() || - TT.isOSBinFormatMachO() || TT.isOSBinFormatXCOFF()) + TT.isOSBinFormatMachO() || TT.isOSBinFormatXCOFF() || + TT.isOSBinFormatWasm()) return false; return true; diff --git a/llvm/test/CodeGen/WebAssembly/profile.ll b/llvm/test/CodeGen/WebAssembly/profile.ll new file mode 100644 index 0000000000000..27802cc3bf356 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/profile.ll @@ -0,0 +1,47 @@ +; RUN: llc < %s --filetype=obj | obj2yaml | FileCheck %s + +target triple = "wasm32-unknown-unknown" + +$__covrec_A = comdat any +$__covrec_B = comdat any + +@__covrec_A = linkonce_odr hidden constant <{ i64, i32, i64, i64, [4 x i8] }> <{ + i64 -1978722966671112904, + i32 4, + i64 0, + i64 -8102528905418564625, + [4 x i8] c"\01\01\04\11" +}>, section "__llvm_covfun", comdat, align 8 +@__covrec_B = linkonce_odr hidden constant <{ i64, i32, i64, i64, [4 x i8] }> <{ + i64 8006510647218728891, + i32 9, + i64 0, + i64 -8102528905418564625, + [4 x i8] c"\01\01\00\01" +}>, section "__llvm_covfun", comdat, align 8 +@__llvm_coverage_mapping = private constant { { i32, i32, i32, i32 }, [4 x i8] } { + { i32, i32, i32, i32 } { i32 0, i32 87, i32 0, i32 5 }, + [4 x i8] c"\01\01\00\02" +}, section "__llvm_covmap", align 8 + +; CHECK: - Type: CUSTOM +; CHECK-NEXT: Name: __llvm_covfun +; CHECK-NEXT: Payload: 3845A90EF2298AE4040000000000000000000000EF1B31BAE3088E8F01010411 +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: __llvm_covfun +; CHECK-NEXT: Payload: BBEFDA6903D71C6F090000000000000000000000EF1B31BAE3088E8F01010001 +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: __llvm_covmap +; CHECK-NEXT: Payload: '0000000057000000000000000500000001010002' +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: linking +; CHECK-NEXT: Version: 2 +; CHECK-NEXT: Comdats: +; CHECK-NEXT: - Name: __covrec_A +; CHECK-NEXT: Entries: +; CHECK-NEXT: - Kind: SECTION +; CHECK-NEXT: Index: 1 +; CHECK-NEXT: - Name: __covrec_B +; CHECK-NEXT: Entries: +; CHECK-NEXT: - Kind: SECTION +; CHECK-NEXT: Index: 2 diff --git a/llvm/test/Instrumentation/InstrProfiling/profiling.ll b/llvm/test/Instrumentation/InstrProfiling/profiling.ll index e7678a9dce089..74dd54cef50fb 100644 --- a/llvm/test/Instrumentation/InstrProfiling/profiling.ll +++ b/llvm/test/Instrumentation/InstrProfiling/profiling.ll @@ -114,11 +114,6 @@ declare void @llvm.instrprof.increment(ptr, i64, i32, i32) ; PS: %[[REG:.*]] = load i32, ptr @__llvm_profile_runtime ; XCOFF-NOT: define .* __llvm_profile_runtime_user -; WASM: define internal void @__llvm_profile_register_functions() unnamed_addr { -; WASM-NEXT: call void @__llvm_profile_register_function(ptr @__profd_foo) -; WASM-NEXT: call void @__llvm_profile_register_function(ptr @__profd_foo_weak) -; WASM: call void @__llvm_profile_register_names_function(ptr @__llvm_prf_nm -; WASM-NEXT: ret void -; WASM-NEXT: } +; WASM-NOT: internal void @__llvm_profile_register_functions() ; XCOFF-NOT: internal void @__llvm_profile_register_functions() diff --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32 b/llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32 new file mode 100755 index 0000000000000..5a606d5a2f69f Binary files /dev/null and b/llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32 differ diff --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.wasm.proftext b/llvm/test/tools/llvm-cov/Inputs/binary-formats.wasm.proftext new file mode 100644 index 0000000000000..20fc3816c2255 --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/binary-formats.wasm.proftext @@ -0,0 +1,4 @@ +__main_argc_argv +0x0 +1 +100 diff --git a/llvm/test/tools/llvm-cov/binary-formats.c b/llvm/test/tools/llvm-cov/binary-formats.c index a5bfc012860ec..bb61b288cfc62 100644 --- a/llvm/test/tools/llvm-cov/binary-formats.c +++ b/llvm/test/tools/llvm-cov/binary-formats.c @@ -10,4 +10,11 @@ int main(int argc, const char *argv[]) {} // RUN: llvm-cov show %S/Inputs/binary-formats.v3.macho64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s // RUN: llvm-cov show %S/Inputs/binary-formats.v6.linux64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s +// RUN: llvm-profdata merge %S/Inputs/binary-formats.wasm.proftext -o %t.wasm.profdata +// NOTE: The wasm binary is built with the following command: +// clang -target wasm32-unknown-wasi %s -o %S/Inputs/binary-formats.v6.wasm32 \ +// -mllvm -enable-name-compression=false \ +// -fprofile-instr-generate -fcoverage-mapping -lwasi-emulated-getpid -lwasi-emulated-mman +// RUN: llvm-cov show %S/Inputs/binary-formats.v6.wasm32 -instr-profile %t.wasm.profdata -path-equivalence=/tmp,%S %s | FileCheck %s + // RUN: llvm-cov export %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json