Author: Yuta Saito
Date: 2024-10-15T02:41:43+09:00
New Revision: d4efc3e097f40afbe8ae275150f49bb08fc04572

URL: 
https://github.com/llvm/llvm-project/commit/d4efc3e097f40afbe8ae275150f49bb08fc04572
DIFF: 
https://github.com/llvm/llvm-project/commit/d4efc3e097f40afbe8ae275150f49bb08fc04572.diff

LOG: [Coverage][WebAssembly] Add initial support for WebAssembly/WASI (#111332)

Currently, WebAssembly/WASI target does not provide direct support for
code coverage.
This patch set fixes several issues to unlock the feature. The main
changes are:

1. Port `compiler-rt/lib/profile` to WebAssembly/WASI.
2. Adjust profile metadata sections for Wasm object file format.
- [CodeGen] Emit `__llvm_covmap` and `__llvm_covfun` as custom sections
instead of data segments.
    - [lld] Align the interval space of custom sections at link time.
- [llvm-cov] Copy misaligned custom section data if the start address is
not aligned.
    - [llvm-cov] Read `__llvm_prf_names` from data segments
3. [clang] Link with profile runtime libraries if requested

See each commit message for more details and rationale.
This is part of the effort to add code coverage support in Wasm target
of Swift toolchain.

Added: 
    lld/test/wasm/custom-section-align.s
    llvm/test/CodeGen/WebAssembly/profile.ll
    llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32
    llvm/test/tools/llvm-cov/Inputs/binary-formats.wasm.proftext

Modified: 
    clang/lib/Driver/ToolChains/WebAssembly.cpp
    compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
    compiler-rt/cmake/config-ix.cmake
    compiler-rt/lib/profile/CMakeLists.txt
    compiler-rt/lib/profile/GCDAProfiling.c
    compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
    compiler-rt/lib/profile/InstrProfilingPlatformOther.c
    compiler-rt/lib/profile/InstrProfilingPort.h
    compiler-rt/lib/profile/InstrProfilingUtil.c
    lld/wasm/InputChunks.h
    lld/wasm/InputFiles.cpp
    lld/wasm/OutputSections.cpp
    llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
    llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
    llvm/lib/MC/MCContext.cpp
    llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
    llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
    llvm/test/Instrumentation/InstrProfiling/profiling.ll
    llvm/test/tools/llvm-cov/binary-formats.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp 
b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index 9aec11e69fde1d..44a6894d30fb29 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 809e9277156912..d00d39518104bf 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 a93a88a9205001..a494e0532a50bc 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 26178412967201..ac1451c8ceed18 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 <sys/file.h>
+
+int fd;
+int main() {
+  flock(fd, LOCK_EX);
+  return 0;
+}
+
+" COMPILER_RT_TARGET_HAS_FLOCK)
+
 CHECK_CXX_SOURCE_COMPILES("
 #include <sys/utsname.h>
 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 d6e2175169e4a5..f67d95d21a7b54 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 b766436497b741..02f23379ce98bf 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 <elf.h>
 #include <link.h>
 #endif

diff  --git a/compiler-rt/lib/profile/InstrProfilingPlatformOther.c 
b/compiler-rt/lib/profile/InstrProfilingPlatformOther.c
index aa79a5641ceca6..52e82273f8aade 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 <stdlib.h>
 #include <stdio.h>

diff  --git a/compiler-rt/lib/profile/InstrProfilingPort.h 
b/compiler-rt/lib/profile/InstrProfilingPort.h
index f77699ee8d59cf..66d697885eaeee 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 642393d432d7ea..95ec4080ba2504 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 00000000000000..0e46177f4cdb79
--- /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 14eb008c212fb5..d6769bcf5c8232 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 de8e707ab2b497..420865e2aea8e3 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 = &section;
     } else if (section.Type == WASM_SEC_CUSTOM) {
       InputChunk *customSec;
+      uint32_t alignment = getCustomSectionAlignment(section);
       if (shouldMerge(section))
-        customSec = make<MergeInputChunk>(section, this);
+        customSec = make<MergeInputChunk>(section, this, alignment);
       else
-        customSec = make<InputSection>(section, this);
+        customSec = make<InputSection>(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 b0b2446cb56bfc..e4f75829ec4c3e 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 f05b90114d75a6..886b4d3d6894dc 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<MemoryBuffer>;
+  using CoverageMapCopyStorage = std::unique_ptr<MemoryBuffer>;
 
 private:
   std::vector<std::string> 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<InstrProfSymtab> 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<std::unique_ptr<BinaryCoverageReader>>
   createCoverageReaderFromBuffer(
       StringRef Coverage, FuncRecordsStorage &&FuncRecords,
+      CoverageMapCopyStorage &&CoverageMap,
       std::unique_ptr<InstrProfSymtab> ProfileNamesPtr, uint8_t BytesInAddress,
       llvm::endianness Endian, StringRef CompilationDir = "");
 

diff  --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp 
b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 0d3e4ba5662e01..ce50a3c19ffe04 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 ac3946b6ef46f3..b97f9d9f5fed0f 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<MCSymbolWasm>(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 bc4e780fb67a60..461fc43d32f8df 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<std::unique_ptr<BinaryCoverageReader>>
 BinaryCoverageReader::createCoverageReaderFromBuffer(
     StringRef Coverage, FuncRecordsStorage &&FuncRecords,
+    CoverageMapCopyStorage &&CoverageMap,
     std::unique_ptr<InstrProfSymtab> ProfileNamesPtr, uint8_t BytesInAddress,
     llvm::endianness Endian, StringRef CompilationDir) {
   if (ProfileNamesPtr == nullptr)
     return make_error<CoverageMapError>(coveragemap_error::malformed,
                                         "Caller must provide ProfileNames");
-  std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader(
-      std::move(ProfileNamesPtr), std::move(FuncRecords)));
+  std::unique_ptr<BinaryCoverageReader> 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<std::pair<StringRef, uint64_t>>
+lookupAllocatableSection(ObjectFile &OF, InstrProfSectKind IPSK) {
+  // On Wasm, allocatable sections can live only in data segments.
+  if (auto *WOF = dyn_cast<WasmObjectFile>(&OF)) {
+    std::vector<const WasmSegment *> 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<CoverageMapError>(coveragemap_error::malformed);
+      auto &Segment = WOF->dataSegments()[DebugName.Index];
+      Segments.push_back(&Segment);
+    }
+    if (Segments.empty())
+      return make_error<CoverageMapError>(coveragemap_error::no_data_found);
+    if (Segments.size() != 1)
+      return make_error<CoverageMapError>(coveragemap_error::malformed);
+
+    const auto &Segment = *Segments.front();
+    auto &Data = Segment.Data;
+    StringRef Content(reinterpret_cast<const char *>(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<CoverageMapError>(
+        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<std::unique_ptr<BinaryCoverageReader>>
 loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
                  StringRef CompilationDir = "",
@@ -1105,23 +1156,20 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef 
Arch,
 
   // Look for the sections that we are interested in.
   auto ProfileNames = std::make_unique<InstrProfSymtab>();
-  std::vector<SectionRef> 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<CoverageMapError>(
-        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<Binary> 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<MemoryBuffer> 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<Binary> 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 1c95a4606ecc56..929c787442057a 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 00000000000000..27802cc3bf3567
--- /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 e7678a9dce0891..74dd54cef50fb5 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 00000000000000..5a606d5a2f69fd
Binary files /dev/null and 
b/llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32 
diff er

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 00000000000000..20fc3816c2255a
--- /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 a5bfc012860ec3..bb61b288cfc62e 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


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to