Skip to content

Commit 1e7311e

Browse files
[Coverage][WebAssembly] Add initial support for WebAssembly/WASI (llvm#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.
1 parent 3a9722b commit 1e7311e

File tree

23 files changed

+253
-45
lines changed

23 files changed

+253
-45
lines changed

clang/lib/Driver/ToolChains/WebAssembly.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
155155
AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args);
156156
}
157157

158+
ToolChain.addProfileRTLibs(Args, CmdArgs);
159+
158160
CmdArgs.push_back("-o");
159161
CmdArgs.push_back(Output.getFilename());
160162

compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ set(ALL_HWASAN_SUPPORTED_ARCH ${X86_64} ${ARM64} ${RISCV64})
7171
set(ALL_MEMPROF_SUPPORTED_ARCH ${X86_64})
7272
set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC32} ${PPC64}
7373
${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
74-
${RISCV32} ${RISCV64} ${LOONGARCH64})
74+
${RISCV32} ${RISCV64} ${LOONGARCH64} ${WASM32})
7575
set(ALL_CTX_PROFILE_SUPPORTED_ARCH ${X86_64})
7676
set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64} ${S390X}
7777
${LOONGARCH64} ${RISCV64})

compiler-rt/cmake/config-ix.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ else()
816816
endif()
817817

818818
if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
819-
OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX")
819+
OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX|WASI")
820820
set(COMPILER_RT_HAS_PROFILE TRUE)
821821
else()
822822
set(COMPILER_RT_HAS_PROFILE FALSE)

compiler-rt/lib/profile/CMakeLists.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ int main() {
3838
3939
" COMPILER_RT_TARGET_HAS_FCNTL_LCK)
4040

41+
CHECK_CXX_SOURCE_COMPILES("
42+
#include <sys/file.h>
43+
44+
int fd;
45+
int main() {
46+
flock(fd, LOCK_EX);
47+
return 0;
48+
}
49+
50+
" COMPILER_RT_TARGET_HAS_FLOCK)
51+
4152
CHECK_CXX_SOURCE_COMPILES("
4253
#include <sys/utsname.h>
4354
int main() {
@@ -93,6 +104,13 @@ if(FUCHSIA OR UNIX)
93104
-Wno-pedantic)
94105
endif()
95106

107+
if(CMAKE_SYSTEM_NAME STREQUAL "WASI")
108+
set(EXTRA_FLAGS
109+
${EXTRA_FLAGS}
110+
-D_WASI_EMULATED_MMAN
111+
-D_WASI_EMULATED_GETPID)
112+
endif()
113+
96114
if(COMPILER_RT_TARGET_HAS_ATOMICS)
97115
set(EXTRA_FLAGS
98116
${EXTRA_FLAGS}
@@ -105,6 +123,12 @@ if(COMPILER_RT_TARGET_HAS_FCNTL_LCK)
105123
-DCOMPILER_RT_HAS_FCNTL_LCK=1)
106124
endif()
107125

126+
if(COMPILER_RT_TARGET_HAS_FLOCK)
127+
set(EXTRA_FLAGS
128+
${EXTRA_FLAGS}
129+
-DCOMPILER_RT_HAS_FLOCK=1)
130+
endif()
131+
108132
if(COMPILER_RT_TARGET_HAS_UNAME)
109133
set(EXTRA_FLAGS
110134
${EXTRA_FLAGS}

compiler-rt/lib/profile/GCDAProfiling.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ void llvm_reset_counters(void) {
584584
}
585585
}
586586

587-
#if !defined(_WIN32)
587+
#if !defined(_WIN32) && !defined(__wasm__)
588588
COMPILER_RT_VISIBILITY
589589
pid_t __gcov_fork() {
590590
pid_t parent_pid = getpid();

compiler-rt/lib/profile/InstrProfilingPlatformLinux.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
|*
77
\*===----------------------------------------------------------------------===*/
88

9-
#if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
10-
(defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__) || \
11-
defined(_AIX)
9+
#if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
10+
(defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__) || \
11+
defined(_AIX) || defined(__wasm__)
1212

13-
#if !defined(_AIX)
13+
#if !defined(_AIX) && !defined(__wasm__)
1414
#include <elf.h>
1515
#include <link.h>
1616
#endif

compiler-rt/lib/profile/InstrProfilingPlatformOther.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__) && \
1010
!defined(__Fuchsia__) && !(defined(__sun__) && defined(__svr4__)) && \
11-
!defined(__NetBSD__) && !defined(_WIN32) && !defined(_AIX)
11+
!defined(__NetBSD__) && !defined(_WIN32) && !defined(_AIX) && \
12+
!defined(__wasm__)
1213

1314
#include <stdlib.h>
1415
#include <stdio.h>

compiler-rt/lib/profile/InstrProfilingPort.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
#endif
5555

5656
#define COMPILER_RT_MAX_HOSTLEN 128
57-
#ifdef __ORBIS__
57+
#if defined(__ORBIS__) || defined(__wasi__)
5858
#define COMPILER_RT_GETHOSTNAME(Name, Len) ((void)(Name), (void)(Len), (-1))
5959
#else
6060
#define COMPILER_RT_GETHOSTNAME(Name, Len) lprofGetHostName(Name, Len)

compiler-rt/lib/profile/InstrProfilingUtil.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,11 @@ COMPILER_RT_VISIBILITY int lprofLockFd(int fd) {
153153
}
154154
}
155155
return 0;
156-
#else
156+
#elif defined(COMPILER_RT_HAS_FLOCK)
157157
flock(fd, LOCK_EX);
158158
return 0;
159+
#else
160+
return 0;
159161
#endif
160162
}
161163

@@ -178,9 +180,11 @@ COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) {
178180
}
179181
}
180182
return 0;
181-
#else
183+
#elif defined(COMPILER_RT_HAS_FLOCK)
182184
flock(fd, LOCK_UN);
183185
return 0;
186+
#else
187+
return 0;
184188
#endif
185189
}
186190

@@ -372,8 +376,8 @@ COMPILER_RT_VISIBILITY void lprofRestoreSigKill(void) {
372376

373377
COMPILER_RT_VISIBILITY int lprofReleaseMemoryPagesToOS(uintptr_t Begin,
374378
uintptr_t End) {
375-
#if defined(__ve__)
376-
// VE doesn't support madvise.
379+
#if defined(__ve__) || defined(__wasi__)
380+
// VE and WASI doesn't support madvise.
377381
return 0;
378382
#else
379383
size_t PageSize = getpagesize();

lld/test/wasm/custom-section-align.s

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
2+
# RUN: wasm-ld --no-entry %t.o -o %t.wasm
3+
# RUN: obj2yaml %t.wasm | FileCheck %s
4+
5+
# Check that "__llvm_covfun" custom section is aligned to 8 bytes.
6+
7+
.section .custom_section.__llvm_covfun,"GR",@,__covrec_A
8+
.int32 1
9+
.int8 2
10+
# pad .int8 0
11+
# .int8 0
12+
# .int8 0
13+
14+
.section .custom_section.__llvm_covfun,"GR",@,__covrec_B
15+
.int32 3
16+
17+
# CHECK: - Type: CUSTOM
18+
# CHECK-NEXT: Name: __llvm_covfun
19+
# CHECK-NEXT: Payload: '010000000200000003000000'
20+
21+
# Check that regular custom sections are not aligned.
22+
.section .custom_section.foo,"GR",@,foo_A
23+
.int32 1
24+
.int8 2
25+
26+
.section .custom_section.foo,"GR",@,foo_B
27+
.int32 3
28+
29+
# CHECK: - Type: CUSTOM
30+
# CHECK-NEXT: Name: foo
31+
# CHECK-NEXT: Payload: '010000000203000000'

lld/wasm/InputChunks.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,9 @@ class MergeInputChunk : public InputChunk {
177177
inputSectionOffset = seg.SectionOffset;
178178
}
179179

180-
MergeInputChunk(const WasmSection &s, ObjFile *f)
181-
: InputChunk(f, Merge, s.Name, 0, llvm::wasm::WASM_SEG_FLAG_STRINGS) {
180+
MergeInputChunk(const WasmSection &s, ObjFile *f, uint32_t alignment)
181+
: InputChunk(f, Merge, s.Name, alignment,
182+
llvm::wasm::WASM_SEG_FLAG_STRINGS) {
182183
assert(s.Type == llvm::wasm::WASM_SEC_CUSTOM);
183184
comdat = s.Comdat;
184185
rawData = s.Content;
@@ -234,6 +235,7 @@ class SyntheticMergedChunk : public InputChunk {
234235

235236
void addMergeChunk(MergeInputChunk *ms) {
236237
comdat = ms->getComdat();
238+
alignment = std::max(alignment, ms->alignment);
237239
ms->parent = this;
238240
chunks.push_back(ms);
239241
}
@@ -337,8 +339,8 @@ class SyntheticFunction : public InputFunction {
337339
// Represents a single Wasm Section within an input file.
338340
class InputSection : public InputChunk {
339341
public:
340-
InputSection(const WasmSection &s, ObjFile *f)
341-
: InputChunk(f, InputChunk::Section, s.Name),
342+
InputSection(const WasmSection &s, ObjFile *f, uint32_t alignment)
343+
: InputChunk(f, InputChunk::Section, s.Name, alignment),
342344
tombstoneValue(getTombstoneForSection(s.Name)), section(s) {
343345
assert(section.Type == llvm::wasm::WASM_SEC_CUSTOM);
344346
comdat = section.Comdat;

lld/wasm/InputFiles.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/BinaryFormat/Wasm.h"
1919
#include "llvm/Object/Binary.h"
2020
#include "llvm/Object/Wasm.h"
21+
#include "llvm/ProfileData/InstrProf.h"
2122
#include "llvm/Support/Path.h"
2223
#include "llvm/Support/TarWriter.h"
2324
#include "llvm/Support/raw_ostream.h"
@@ -451,6 +452,18 @@ void SharedFile::parse() {
451452
}
452453
}
453454

455+
// Returns the alignment for a custom section. This is used to concatenate
456+
// custom sections with the same name into a single custom section.
457+
static uint32_t getCustomSectionAlignment(const WasmSection &sec) {
458+
// TODO: Add a section attribute for alignment in the linking spec.
459+
if (sec.Name == getInstrProfSectionName(IPSK_covfun, Triple::Wasm) ||
460+
sec.Name == getInstrProfSectionName(IPSK_covmap, Triple::Wasm)) {
461+
// llvm-cov assumes that coverage metadata sections are 8-byte aligned.
462+
return 8;
463+
}
464+
return 1;
465+
}
466+
454467
WasmFileBase::WasmFileBase(Kind k, MemoryBufferRef m) : InputFile(k, m) {
455468
// Parse a memory buffer as a wasm file.
456469
LLVM_DEBUG(dbgs() << "Reading object: " << toString(this) << "\n");
@@ -520,10 +533,11 @@ void ObjFile::parse(bool ignoreComdats) {
520533
dataSection = &section;
521534
} else if (section.Type == WASM_SEC_CUSTOM) {
522535
InputChunk *customSec;
536+
uint32_t alignment = getCustomSectionAlignment(section);
523537
if (shouldMerge(section))
524-
customSec = make<MergeInputChunk>(section, this);
538+
customSec = make<MergeInputChunk>(section, this, alignment);
525539
else
526-
customSec = make<InputSection>(section, this);
540+
customSec = make<InputSection>(section, this, alignment);
527541
customSec->discarded = isExcludedByComdat(customSec);
528542
customSections.emplace_back(customSec);
529543
customSections.back()->setRelocations(section.Relocations);

lld/wasm/OutputSections.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ void CustomSection::finalizeContents() {
249249

250250
for (InputChunk *section : inputSections) {
251251
assert(!section->discarded);
252+
payloadSize = alignTo(payloadSize, section->alignment);
252253
section->outSecOff = payloadSize;
253254
payloadSize += section->getSize();
254255
}

llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ class BinaryCoverageReader : public CoverageMappingReader {
180180
};
181181

182182
using FuncRecordsStorage = std::unique_ptr<MemoryBuffer>;
183+
using CoverageMapCopyStorage = std::unique_ptr<MemoryBuffer>;
183184

184185
private:
185186
std::vector<std::string> Filenames;
@@ -195,9 +196,16 @@ class BinaryCoverageReader : public CoverageMappingReader {
195196
// D69471, which can split up function records into multiple sections on ELF.
196197
FuncRecordsStorage FuncRecords;
197198

199+
// Used to tie the lifetimes of an optional copy of the coverage mapping data
200+
// to the lifetime of this BinaryCoverageReader instance. Needed to support
201+
// Wasm object format, which might require realignment of section contents.
202+
CoverageMapCopyStorage CoverageMapCopy;
203+
198204
BinaryCoverageReader(std::unique_ptr<InstrProfSymtab> Symtab,
199-
FuncRecordsStorage &&FuncRecords)
200-
: ProfileNames(std::move(Symtab)), FuncRecords(std::move(FuncRecords)) {}
205+
FuncRecordsStorage &&FuncRecords,
206+
CoverageMapCopyStorage &&CoverageMapCopy)
207+
: ProfileNames(std::move(Symtab)), FuncRecords(std::move(FuncRecords)),
208+
CoverageMapCopy(std::move(CoverageMapCopy)) {}
201209

202210
public:
203211
BinaryCoverageReader(const BinaryCoverageReader &) = delete;
@@ -212,6 +220,7 @@ class BinaryCoverageReader : public CoverageMappingReader {
212220
static Expected<std::unique_ptr<BinaryCoverageReader>>
213221
createCoverageReaderFromBuffer(
214222
StringRef Coverage, FuncRecordsStorage &&FuncRecords,
223+
CoverageMapCopyStorage &&CoverageMap,
215224
std::unique_ptr<InstrProfSymtab> ProfileNamesPtr, uint8_t BytesInAddress,
216225
llvm::endianness Endian, StringRef CompilationDir = "");
217226

llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2173,7 +2173,11 @@ MCSection *TargetLoweringObjectFileWasm::getExplicitSectionGlobal(
21732173
// This could be avoided if all data segements (the wasm sense) were
21742174
// represented as their own sections (in the llvm sense).
21752175
// TODO(sbc): https://github.com/WebAssembly/tool-conventions/issues/138
2176-
if (Name == ".llvmcmd" || Name == ".llvmbc")
2176+
if (Name == getInstrProfSectionName(IPSK_covmap, Triple::Wasm,
2177+
/*AddSegmentInfo=*/false) ||
2178+
Name == getInstrProfSectionName(IPSK_covfun, Triple::Wasm,
2179+
/*AddSegmentInfo=*/false) ||
2180+
Name == ".llvmbc" || Name == ".llvmcmd")
21772181
Kind = SectionKind::getMetadata();
21782182

21792183
StringRef Group = "";

llvm/lib/MC/MCContext.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,11 @@ MCSectionWasm *MCContext::getWasmSection(const Twine &Section, SectionKind K,
756756
if (!Group.isTriviallyEmpty() && !Group.str().empty()) {
757757
GroupSym = cast<MCSymbolWasm>(getOrCreateSymbol(Group));
758758
GroupSym->setComdat(true);
759+
if (K.isMetadata() && !GroupSym->getType().has_value()) {
760+
// Comdat group symbol associated with a custom section is a section
761+
// symbol (not a data symbol).
762+
GroupSym->setType(wasm::WASM_SYMBOL_TYPE_SECTION);
763+
}
759764
}
760765

761766
return getWasmSection(Section, K, Flags, GroupSym, UniqueID);

0 commit comments

Comments
 (0)