Skip to content

Commit d4efc3e

Browse files
[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.
1 parent 2f077ec commit d4efc3e

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
@@ -163,6 +163,8 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
163163
AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args);
164164
}
165165

166+
ToolChain.addProfileRTLibs(Args, CmdArgs);
167+
166168
CmdArgs.push_back("-o");
167169
CmdArgs.push_back(Output.getFilename());
168170

compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ set(ALL_HWASAN_SUPPORTED_ARCH ${X86_64} ${ARM64} ${RISCV64})
7777
set(ALL_MEMPROF_SUPPORTED_ARCH ${X86_64})
7878
set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC32} ${PPC64}
7979
${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
80-
${RISCV32} ${RISCV64} ${LOONGARCH64})
80+
${RISCV32} ${RISCV64} ${LOONGARCH64} ${WASM32})
8181
set(ALL_CTX_PROFILE_SUPPORTED_ARCH ${X86_64})
8282
if (OS_NAME MATCHES "FreeBSD")
8383
set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})

compiler-rt/cmake/config-ix.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@ else()
822822
endif()
823823

824824
if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
825-
OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX")
825+
OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD|AIX|WASI")
826826
set(COMPILER_RT_HAS_PROFILE TRUE)
827827
else()
828828
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
@@ -152,9 +152,11 @@ COMPILER_RT_VISIBILITY int lprofLockFd(int fd) {
152152
}
153153
}
154154
return 0;
155-
#else
155+
#elif defined(COMPILER_RT_HAS_FLOCK)
156156
flock(fd, LOCK_EX);
157157
return 0;
158+
#else
159+
return 0;
158160
#endif
159161
}
160162

@@ -177,9 +179,11 @@ COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) {
177179
}
178180
}
179181
return 0;
180-
#else
182+
#elif defined(COMPILER_RT_HAS_FLOCK)
181183
flock(fd, LOCK_UN);
182184
return 0;
185+
#else
186+
return 0;
183187
#endif
184188
}
185189

@@ -353,8 +357,8 @@ COMPILER_RT_VISIBILITY void lprofRestoreSigKill(void) {
353357

354358
COMPILER_RT_VISIBILITY int lprofReleaseMemoryPagesToOS(uintptr_t Begin,
355359
uintptr_t End) {
356-
#if defined(__ve__)
357-
// VE doesn't support madvise.
360+
#if defined(__ve__) || defined(__wasi__)
361+
// VE and WASI doesn't support madvise.
358362
return 0;
359363
#else
360364
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
@@ -244,6 +244,7 @@ void CustomSection::finalizeContents() {
244244

245245
for (InputChunk *section : inputSections) {
246246
assert(!section->discarded);
247+
payloadSize = alignTo(payloadSize, section->alignment);
247248
section->outSecOff = payloadSize;
248249
payloadSize += section->getSize();
249250
}

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
@@ -2171,7 +2171,11 @@ MCSection *TargetLoweringObjectFileWasm::getExplicitSectionGlobal(
21712171
// This could be avoided if all data segements (the wasm sense) were
21722172
// represented as their own sections (in the llvm sense).
21732173
// TODO(sbc): https://github.com/WebAssembly/tool-conventions/issues/138
2174-
if (Name == ".llvmcmd" || Name == ".llvmbc")
2174+
if (Name == getInstrProfSectionName(IPSK_covmap, Triple::Wasm,
2175+
/*AddSegmentInfo=*/false) ||
2176+
Name == getInstrProfSectionName(IPSK_covfun, Triple::Wasm,
2177+
/*AddSegmentInfo=*/false) ||
2178+
Name == ".llvmbc" || Name == ".llvmcmd")
21752179
Kind = SectionKind::getMetadata();
21762180

21772181
StringRef Group = "";

llvm/lib/MC/MCContext.cpp

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

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

0 commit comments

Comments
 (0)