Skip to content

Commit 2c49830

Browse files
committed
[LLVM][WebAssembly] Parse code metadata entries in wasm-ld to discard hints for removed functions
1 parent 7cca0fe commit 2c49830

File tree

10 files changed

+209
-72
lines changed

10 files changed

+209
-72
lines changed

lld/test/wasm/code-metadata-branch-hints.ll

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
# RUN: rm -rf %t; split-file %s %t
2-
; RUN: llc -mcpu=mvp -mtriple=wasm32-unknown-unknown -filetype=obj %t/f1.ll -o %t/f1.o -mattr=+branch-hinting
3-
; RUN: llc -mcpu=mvp -mtriple=wasm32-unknown-unknown -filetype=obj %t/f2.ll -o %t/f2.o -mattr=+branch-hinting
4-
; RUN: wasm-ld --export-all -o %t.wasm %t/f2.o %t/f1.o
5-
; RUN: obj2yaml %t.wasm | FileCheck --check-prefixes=CHECK %s
62

7-
; RUN: llc -mcpu=mvp -mtriple=wasm32-unknown-unknown -filetype=obj %t/f1.ll -o %t/f1.o -mattr=-branch-hinting
8-
; RUN: llc -mcpu=mvp -mtriple=wasm32-unknown-unknown -filetype=obj %t/f2.ll -o %t/f2.o -mattr=-branch-hinting
3+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4+
;; with branch hints
5+
; RUN: llc -mcpu=mvp -filetype=obj %t/f1.ll -o %t/f1.o -mattr=+branch-hinting
6+
; RUN: llc -mcpu=mvp -filetype=obj %t/f2.ll -o %t/f2.o -mattr=+branch-hinting
97
; RUN: wasm-ld --export-all -o %t.wasm %t/f2.o %t/f1.o
10-
; RUN: obj2yaml %t.wasm | FileCheck --check-prefixes=NCHECK %s
8+
; RUN: obj2yaml %t.wasm | FileCheck --check-prefixes=CHECK %s
119

1210
; CHECK: - Type: CUSTOM
1311
; CHECK: Name: metadata.code.branch_hint
@@ -56,11 +54,49 @@
5654
; CHECK-NEXT: - Prefix: USED
5755
; CHECK-NEXT: Name: branch-hinting
5856

57+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
58+
;; without branch hints
59+
; RUN: llc -mcpu=mvp -filetype=obj %t/f1.ll -o %t/f1.o -mattr=-branch-hinting
60+
; RUN: llc -mcpu=mvp -filetype=obj %t/f2.ll -o %t/f2.o -mattr=-branch-hinting
61+
; RUN: wasm-ld --export-all -o %t.wasm %t/f2.o %t/f1.o
62+
; RUN: obj2yaml %t.wasm | FileCheck --check-prefixes=NCHECK %s
5963

6064
; NCHECK-NOT: Name: metadata.code.branch_hint
6165
; NCHECK-NOT: Name: branch-hinting
6266

67+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
68+
;; with branch hints, but only the _start function is not removed by lld (no --export-all)
69+
; RUN: llc -mcpu=mvp -filetype=obj %t/f1.ll -o %t/f1.o -mattr=+branch-hinting
70+
; RUN: llc -mcpu=mvp -filetype=obj %t/f2.ll -o %t/f2.o -mattr=+branch-hinting
71+
; RUN: wasm-ld -o %t.wasm %t/f2.o %t/f1.o
72+
; RUN: obj2yaml %t.wasm | FileCheck --check-prefixes=RCHECK %s
73+
74+
; RCHECK: - Type: CUSTOM
75+
; RCHECK: Name: metadata.code.branch_hint
76+
; RCHECK-NEXT: Entries:
77+
; RCHECK-NEXT: - FuncIdx: 0
78+
; RCHECK-NEXT: Hints:
79+
; RCHECK-NEXT: - Offset: 5
80+
; RCHECK-NEXT: Size: 1
81+
; RCHECK-NEXT: Data: UNLIKELY
82+
; RCHECK-NEXT: - Type: CODE
83+
84+
; RCHECK: - Type: CUSTOM
85+
; RCHECK-NEXT: Name: name
86+
; RCHECK-NEXT: FunctionNames:
87+
; RCHECK-NEXT: - Index: 0
88+
; RCHECK-NEXT: Name: _start
89+
90+
; RCHECK: - Type: CUSTOM
91+
; RCHECK: Name: target_features
92+
; RCHECK-NEXT: Features:
93+
; RCHECK-NEXT: - Prefix: USED
94+
; RCHECK-NEXT: Name: branch-hinting
95+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
96+
6397
#--- f1.ll
98+
target triple = "wasm32-unknown-unknown"
99+
64100
define i32 @_start(i32 %a) {
65101
entry:
66102
%cmp = icmp eq i32 %a, 0

lld/wasm/InputChunks.cpp

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,9 @@ uint64_t InputChunk::getTombstone() const {
206206
if (const auto *s = dyn_cast<InputSection>(this)) {
207207
return s->tombstoneValue;
208208
}
209-
209+
if (const auto *s = dyn_cast<CodeMetaDataInputSection>(this)) {
210+
return s->tombstoneValue;
211+
}
210212
return 0;
211213
}
212214

@@ -548,10 +550,96 @@ uint64_t InputSection::getTombstoneForSection(StringRef name) {
548550
// 0 is a valid function index.
549551
if (name.starts_with("llvm.func_attr."))
550552
return UINT64_C(-1);
551-
// Returning 0 means there is no tombstone value for this section, and relocation
552-
// will just use the addend.
553+
if (name.starts_with("metadata.code."))
554+
return UINT64_C(-1);
555+
// Returning 0 means there is no tombstone value for this section, and
556+
// relocation will just use the addend.
553557
return 0;
554558
}
555559

560+
void CodeMetaDataInputSection::finalizeContents() {
561+
RelocatedData.resize(data().size());
562+
memcpy(RelocatedData.data(), data().data(), data().size());
563+
relocate(RelocatedData.data());
564+
565+
const uint8_t *Buf = RelocatedData.data();
566+
const uint8_t *End = Buf + RelocatedData.size();
567+
568+
const char *Success = nullptr;
569+
unsigned NumFunctionsEncodingSize;
570+
const unsigned NumFunctions =
571+
decodeULEB128(Buf, &NumFunctionsEncodingSize, End, &Success);
572+
if (Success != nullptr) {
573+
fatal("Failed to decode number of functions in " + name + ": " + Success);
574+
}
575+
Buf += NumFunctionsEncodingSize;
576+
577+
for (unsigned i = 0; i < NumFunctions; ++i) {
578+
const uint8_t *FunctionStartOffset = Buf;
579+
580+
unsigned FuncIdxEncodingSize;
581+
const unsigned FuncIndex =
582+
decodeULEB128(Buf, &FuncIdxEncodingSize, End, &Success);
583+
if (Success != nullptr) {
584+
fatal("Failed to decode function index in " + name + ": " + Success);
585+
}
586+
Buf += FuncIdxEncodingSize;
587+
588+
unsigned NumHintsEncodingSize;
589+
const unsigned NumHints =
590+
decodeULEB128(Buf, &NumHintsEncodingSize, End, &Success);
591+
if (Success != nullptr) {
592+
fatal("Failed to decode hint size in " + name + ": " + Success);
593+
}
594+
Buf += NumHintsEncodingSize;
595+
for (unsigned j = 0; j < NumHints; ++j) {
596+
unsigned OffsetEncodingSize;
597+
decodeULEB128(Buf, &OffsetEncodingSize, End, &Success);
598+
if (Success != nullptr) {
599+
fatal("Failed to decode hint opcode in " + name + ": " + Success);
600+
}
601+
Buf += OffsetEncodingSize;
602+
603+
unsigned HintSizeEncodingSize;
604+
const unsigned HintSize =
605+
decodeULEB128(Buf, &HintSizeEncodingSize, End, &Success);
606+
if (Success != nullptr) {
607+
fatal("Failed to decode hint operand in " + name + ": " + Success);
608+
}
609+
Buf += HintSizeEncodingSize;
610+
Buf += HintSize;
611+
}
612+
// skip entries for removed functions
613+
// this is the whole reason we need to parse the hints here again
614+
if (FuncIndex == static_cast<uint32_t>(getTombstone())) {
615+
continue;
616+
}
617+
ArrayRef FunctionData(FunctionStartOffset, Buf - FunctionStartOffset);
618+
Hints.emplace_back(FunctionData);
619+
}
620+
assert(Buf == End && "CodeMetaDataInputSection: not all data was consumed");
621+
}
622+
623+
void CodeMetaDataInputSection::writeTo(uint8_t *Buf) const {
624+
// write sorted hints to output buffer
625+
// hints are implicitly sorted, since function indices are assigned by
626+
// section order
627+
for (const auto &ref : Hints) {
628+
memcpy(Buf + outSecOff, ref.data(), ref.size());
629+
Buf += ref.size();
630+
}
631+
}
632+
633+
uint32_t CodeMetaDataInputSection::getSize() const {
634+
uint32_t size = 0;
635+
for (const auto &ref : Hints) {
636+
size += ref.size();
637+
}
638+
return size;
639+
}
640+
641+
uint32_t CodeMetaDataInputSection::getNumFuncHints() const {
642+
return Hints.size();
643+
}
556644
} // namespace wasm
557645
} // namespace lld

lld/wasm/InputChunks.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class InputChunk {
4545
Function,
4646
SyntheticFunction,
4747
Section,
48+
CodeMetadata,
4849
};
4950

5051
StringRef name;
@@ -359,6 +360,26 @@ class InputSection : public InputChunk {
359360
const WasmSection &section;
360361
};
361362

363+
class CodeMetaDataInputSection : public InputSection {
364+
public:
365+
CodeMetaDataInputSection(const WasmSection &s, ObjFile *f, uint32_t alignment)
366+
: InputSection(s, f, alignment) {
367+
sectionKind = CodeMetadata;
368+
}
369+
370+
static bool classof(const InputChunk *c) {
371+
return c->kind() == InputChunk::CodeMetadata;
372+
}
373+
void finalizeContents();
374+
void writeTo(uint8_t *Buf) const;
375+
uint32_t getSize() const;
376+
uint32_t getNumFuncHints() const;
377+
378+
private:
379+
// func_idx -> generic hints array
380+
SmallVector<ArrayRef<uint8_t>> Hints;
381+
std::vector<uint8_t> RelocatedData;
382+
};
362383
} // namespace wasm
363384

364385
std::string toString(const wasm::InputChunk *);

lld/wasm/InputFiles.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ void ObjFile::parse(bool ignoreComdats) {
545545
uint32_t alignment = getCustomSectionAlignment(section);
546546
if (shouldMerge(section))
547547
customSec = make<MergeInputChunk>(section, this, alignment);
548+
else if (section.Name == "metadata.code.branch_hint")
549+
customSec = make<CodeMetaDataInputSection>(section, this, alignment);
548550
else
549551
customSec = make<InputSection>(section, this, alignment);
550552
customSec->discarded = isExcludedByComdat(customSec);

lld/wasm/OutputSections.cpp

Lines changed: 31 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,19 @@ void CustomSection::writeTo(uint8_t *buf) {
270270
section->writeTo(buf);
271271
}
272272

273-
void CodeMetaDataSection::writeTo(uint8_t *buf) {
273+
uint32_t CustomSection::getNumRelocations() const {
274+
uint32_t count = 0;
275+
for (const InputChunk *inputSect : inputSections)
276+
count += inputSect->getNumLiveRelocations();
277+
return count;
278+
}
279+
280+
void CustomSection::writeRelocations(raw_ostream &os) const {
281+
for (const InputChunk *s : inputSections)
282+
s->writeRelocations(os);
283+
}
284+
285+
void CodeMetaDataOutputSection::writeTo(uint8_t *buf) {
274286
log("writing " + toString(*this) + " offset=" + Twine(offset) +
275287
" size=" + Twine(getSize()) + " chunks=" + Twine(inputSections.size()));
276288

@@ -283,60 +295,33 @@ void CodeMetaDataSection::writeTo(uint8_t *buf) {
283295
memcpy(buf, nameData.data(), nameData.size());
284296
buf += nameData.size();
285297

286-
uint32_t TotalNumHints = 0;
287-
for (const InputChunk *section :
288-
make_range(inputSections.rbegin(), inputSections.rend())) {
289-
section->writeTo(buf);
290-
unsigned EncodingSize;
291-
uint32_t NumHints =
292-
decodeULEB128(buf + section->outSecOff, &EncodingSize, nullptr);
293-
if (EncodingSize != 5) {
294-
fatal("Unexpected encoding size for function hint vec size in " + name +
295-
": must be exactly 5 bytes.");
296-
}
297-
TotalNumHints += NumHints;
298+
// all input sections' outSecOff is relative to the buffer AFTER this leading
299+
// count
300+
buf += encodeULEB128(NumFuncHints, buf);
301+
for (InputChunk *Section : inputSections) {
302+
assert(!Section->discarded);
303+
CodeMetaDataInputSection *CMSec =
304+
dyn_cast<CodeMetaDataInputSection>(Section);
305+
CMSec->writeTo(buf);
298306
}
299-
encodeULEB128(TotalNumHints, buf, 5);
300307
}
301308

302-
void CodeMetaDataSection::finalizeContents() {
303-
finalizeInputSections();
304-
309+
void CodeMetaDataOutputSection::finalizeContents() {
305310
raw_string_ostream os(nameData);
306311
encodeULEB128(name.size(), os);
307312
os << name;
308313

309-
bool firstSection = true;
310-
for (InputChunk *section : inputSections) {
311-
assert(!section->discarded);
312-
payloadSize = alignTo(payloadSize, section->alignment);
313-
if (firstSection) {
314-
section->outSecOff = payloadSize;
315-
payloadSize += section->getSize();
316-
firstSection = false;
317-
} else {
318-
// adjust output offset so that each section write overwrites exactly the
319-
// subsequent section's function hint vector size (which deduplicates)
320-
section->outSecOff = payloadSize - 5;
321-
// payload size should not include the hint vector size, which is deduped
322-
payloadSize += section->getSize() - 5;
323-
}
314+
for (InputChunk *Section : inputSections) {
315+
assert(!Section->discarded);
316+
CodeMetaDataInputSection *CMSec =
317+
dyn_cast<CodeMetaDataInputSection>(Section);
318+
CMSec->finalizeContents();
319+
NumFuncHints += CMSec->getNumFuncHints();
320+
CMSec->outSecOff = payloadSize;
321+
payloadSize += CMSec->getSize();
324322
}
325-
323+
payloadSize += getULEB128Size(NumFuncHints);
326324
createHeader(payloadSize + nameData.size());
327325
}
328-
329-
uint32_t CustomSection::getNumRelocations() const {
330-
uint32_t count = 0;
331-
for (const InputChunk *inputSect : inputSections)
332-
count += inputSect->getNumLiveRelocations();
333-
return count;
334-
}
335-
336-
void CustomSection::writeRelocations(raw_ostream &os) const {
337-
for (const InputChunk *s : inputSections)
338-
s->writeRelocations(os);
339-
}
340-
341326
} // namespace wasm
342327
} // namespace lld

lld/wasm/OutputSections.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,18 @@ class CustomSection : public OutputSection {
132132
std::string nameData;
133133
};
134134

135-
class CodeMetaDataSection : public CustomSection {
135+
class CodeMetaDataOutputSection : public CustomSection {
136136
public:
137-
CodeMetaDataSection(std::string name, ArrayRef<InputChunk *> inputSections)
137+
CodeMetaDataOutputSection(std::string name,
138+
ArrayRef<InputChunk *> inputSections)
138139
: CustomSection(name, inputSections) {}
139140

140141
void writeTo(uint8_t *buf) override;
141142
void finalizeContents() override;
142-
};
143143

144+
private:
145+
uint32_t NumFuncHints = 0;
146+
};
144147
} // namespace wasm
145148
} // namespace lld
146149

lld/wasm/Writer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void Writer::createBranchHintSection() {
187187
if (const auto &Ins = customSectionMapping.find(SectionName);
188188
Ins != customSectionMapping.end()) {
189189
OutputSection *Sec =
190-
make<CodeMetaDataSection>(std::move(SectionName), Ins->second);
190+
make<CodeMetaDataOutputSection>(std::move(SectionName), Ins->second);
191191
if (ctx.arg.relocatable || ctx.arg.emitRelocs) {
192192
auto *sym = make<OutputSectionSymbol>(Sec);
193193
out.linkingSec->addToSymtab(sym);

llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,12 +463,14 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
463463
void WebAssemblyAsmPrinter::emitBranchHintSection() const {
464464
MCSectionWasm *BranchHintsSection = OutContext.getWasmSection(
465465
".custom_section.metadata.code.branch_hint", SectionKind::getMetadata());
466-
OutStreamer->pushSection();
467-
OutStreamer->switchSection(BranchHintsSection);
468466
const uint32_t NumFunctionHints =
469467
std::count_if(BranchHints.begin(), BranchHints.end(),
470468
[](const auto &BHR) { return !BHR.Hints.empty(); });
471-
OutStreamer->emitULEB128IntValue(NumFunctionHints, 5);
469+
if (NumFunctionHints == 0)
470+
return;
471+
OutStreamer->pushSection();
472+
OutStreamer->switchSection(BranchHintsSection);
473+
OutStreamer->emitULEB128IntValue(NumFunctionHints);
472474
for (const auto &BHR : BranchHints) {
473475
if (BHR.Hints.empty())
474476
continue;

0 commit comments

Comments
 (0)