Skip to content

Commit

Permalink
[Coverage][WebAssembly] Add initial support for WebAssembly/WASI (llv…
Browse files Browse the repository at this point in the history
…m#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.
  • Loading branch information
kateinoigakukun authored and EricWF committed Oct 22, 2024
1 parent 7562a58 commit 322812d
Show file tree
Hide file tree
Showing 23 changed files with 253 additions and 45 deletions.
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
24 changes: 24 additions & 0 deletions compiler-rt/lib/profile/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/profile/GCDAProfiling.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 4 additions & 4 deletions compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion compiler-rt/lib/profile/InstrProfilingPlatformOther.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/profile/InstrProfilingPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 8 additions & 4 deletions compiler-rt/lib/profile/InstrProfilingUtil.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
}

Expand Down Expand Up @@ -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();
Expand Down
31 changes: 31 additions & 0 deletions lld/test/wasm/custom-section-align.s
Original file line number Diff line number Diff line change
@@ -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'
10 changes: 6 additions & 4 deletions lld/wasm/InputChunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
18 changes: 16 additions & 2 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/OutputSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
13 changes: 11 additions & 2 deletions llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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 = "");

Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/MC/MCContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 322812d

Please sign in to comment.