Skip to content

Commit 96a4d75

Browse files
committed
[LLVM][WebAssembly] Implement branch hinting proposal
This commit implements the WebAssembly branch hinting proposal, as detailed at https://webassembly.github.io/branch-hinting/metadata/code/binary.html. This proposal introduces a mechanism to convey branch likelihood information to the WebAssembly engine, allowing for more effective performance optimizations. The proposal specifies a new custom section named `metadata.code.branch_hint`. This section can contain a sequence of hints, where each hint is a single byte that applies to a corresponding `br_if` or `if` instruction. The hint values are: - `0x00` (`unlikely`): The branch is unlikely to be taken. - `0x01` (`likely`): The branch is likely to be taken. This implementation includes the following changes: - Addition of the "branch-hinting" feature (flag) - Collection of edge probabilities in CFGStackify pass - Outputting of `metadata.code.branch_hint` section in WebAssemblyAsmPrinter - Addition of the `WebAssembly::Specifier::S_DEBUG_REF` symbol ref specifier - Custom relaxation of leb128 fragments for storage of uleb128 encoded function indices and instruction offsets - Custom handling of code metadata sections in lld, required since the proposal requires code metadata sections to start with a combined count of function hints, followed by an ordered list of function hints. This change is purely an optimization and does not alter the semantics of WebAssembly programs.
1 parent c861fe8 commit 96a4d75

20 files changed

+440
-8
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5224,6 +5224,8 @@ def mtail_call : Flag<["-"], "mtail-call">, Group<m_wasm_Features_Group>;
52245224
def mno_tail_call : Flag<["-"], "mno-tail-call">, Group<m_wasm_Features_Group>;
52255225
def mwide_arithmetic : Flag<["-"], "mwide-arithmetic">, Group<m_wasm_Features_Group>;
52265226
def mno_wide_arithmetic : Flag<["-"], "mno-wide-arithmetic">, Group<m_wasm_Features_Group>;
5227+
def mbranch_hinting : Flag<["-"], "mbranch-hinting">, Group<m_wasm_Features_Group>;
5228+
def mno_branch_hinting : Flag<["-"], "mno-branch-hinting">, Group<m_wasm_Features_Group>;
52275229
def mexec_model_EQ : Joined<["-"], "mexec-model=">, Group<m_wasm_Features_Driver_Group>,
52285230
Values<"command,reactor">,
52295231
HelpText<"Execution model (WebAssembly only)">,

clang/lib/Basic/Targets/WebAssembly.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
6969
.Case("simd128", SIMDLevel >= SIMD128)
7070
.Case("tail-call", HasTailCall)
7171
.Case("wide-arithmetic", HasWideArithmetic)
72+
.Case("branch-hinting", HasBranchHinting)
7273
.Default(false);
7374
}
7475

@@ -116,6 +117,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
116117
Builder.defineMacro("__wasm_tail_call__");
117118
if (HasWideArithmetic)
118119
Builder.defineMacro("__wasm_wide_arithmetic__");
120+
if (HasBranchHinting)
121+
Builder.defineMacro("__wasm_branch_hinting__");
119122

120123
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
121124
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
@@ -194,6 +197,7 @@ bool WebAssemblyTargetInfo::initFeatureMap(
194197
Features["multimemory"] = true;
195198
Features["tail-call"] = true;
196199
Features["wide-arithmetic"] = true;
200+
Features["branch-hinting"] = true;
197201
setSIMDLevel(Features, RelaxedSIMD, true);
198202
};
199203
if (CPU == "generic") {
@@ -347,6 +351,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
347351
HasWideArithmetic = false;
348352
continue;
349353
}
354+
if (Feature == "+branch-hinting") {
355+
HasBranchHinting = true;
356+
continue;
357+
}
358+
if (Feature == "-branch-hinting") {
359+
HasBranchHinting = false;
360+
continue;
361+
}
350362

351363
Diags.Report(diag::err_opt_not_valid_with_opt)
352364
<< Feature << "-target-feature";

clang/lib/Basic/Targets/WebAssembly.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
7171
bool HasSignExt = false;
7272
bool HasTailCall = false;
7373
bool HasWideArithmetic = false;
74+
bool HasBranchHinting = false;
7475

7576
std::string ABI;
7677

lld/test/wasm/Inputs/branch-hints.ll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
target triple = "wasm32-unknown-unknown"
2+
3+
define i32 @test0(i32 %a) {
4+
entry:
5+
%cmp0 = icmp eq i32 %a, 0
6+
; This metadata hints that the true branch is overwhelmingly likely.
7+
br i1 %cmp0, label %if.then, label %ret1, !prof !0
8+
if.then:
9+
%cmp1 = icmp eq i32 %a, 1
10+
br i1 %cmp1, label %ret1, label %ret2, !prof !1
11+
ret1:
12+
ret i32 2
13+
ret2:
14+
ret i32 1
15+
}
16+
17+
define i32 @test1(i32 %a) {
18+
entry:
19+
%cmp = icmp eq i32 %a, 0
20+
br i1 %cmp, label %if.then, label %if.else, !prof !1
21+
if.then:
22+
ret i32 1
23+
if.else:
24+
ret i32 2
25+
}
26+
27+
; the resulting branch hint is actually reversed, since llvm-br is turned into br_unless, inverting branch probs
28+
!0 = !{!"branch_weights", i32 2000, i32 1}
29+
!1 = !{!"branch_weights", i32 1, i32 2000}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
; RUN: llc -filetype=obj %s -o %t1.o -mattr=+branch-hinting
2+
; RUN: llc -filetype=obj %S/Inputs/branch-hints.ll -o %t2.o -mattr=+branch-hinting
3+
; RUN: wasm-ld --export-all -o %t.wasm %t1.o %t2.o
4+
; RUN: obj2yaml %t.wasm | FileCheck %s
5+
6+
define i32 @_start(i32 %a) {
7+
entry:
8+
%cmp = icmp eq i32 %a, 0
9+
br i1 %cmp, label %if.then, label %if.else, !prof !0
10+
if.then:
11+
ret i32 1
12+
if.else:
13+
ret i32 2
14+
}
15+
16+
define i32 @test_func1(i32 %a) {
17+
entry:
18+
%cmp = icmp eq i32 %a, 0
19+
br i1 %cmp, label %if.then, label %if.else, !prof !1
20+
if.then:
21+
ret i32 1
22+
if.else:
23+
ret i32 2
24+
}
25+
26+
!0 = !{!"branch_weights", i32 2000, i32 1}
27+
!1 = !{!"branch_weights", i32 1, i32 2000}
28+
29+
; CHECK: - Type: CUSTOM
30+
; CHECK-NEXT: Name: metadata.code.branch_hint
31+
; CHECK-NEXT: Payload: '84808080008180808000010501008280808000010501018380808000020701000E0101848080800001050101'
32+
33+
; CHECK: - Type: CUSTOM
34+
; CHECK: Name: target_features
35+
; CHECK-NEXT: Features:
36+
; CHECK: - Prefix: USED
37+
; CHECK-NEXT: Name: branch-hinting

lld/wasm/OutputSections.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,62 @@ void CustomSection::writeTo(uint8_t *buf) {
271271
section->writeTo(buf);
272272
}
273273

274+
void CodeMetaDataSection::writeTo(uint8_t *buf) {
275+
log("writing " + toString(*this) + " offset=" + Twine(offset) +
276+
" size=" + Twine(getSize()) + " chunks=" + Twine(inputSections.size()));
277+
278+
assert(offset);
279+
buf += offset;
280+
281+
// Write section header
282+
memcpy(buf, header.data(), header.size());
283+
buf += header.size();
284+
memcpy(buf, nameData.data(), nameData.size());
285+
buf += nameData.size();
286+
287+
uint32_t TotalNumHints = 0;
288+
for (const InputChunk *section :
289+
make_range(inputSections.rbegin(), inputSections.rend())) {
290+
section->writeTo(buf);
291+
unsigned EncodingSize;
292+
uint32_t NumHints =
293+
decodeULEB128(buf + section->outSecOff, &EncodingSize, nullptr);
294+
if (EncodingSize != 5) {
295+
fatal("Unexpected encoding size for function hint vec size in " + name +
296+
": must be exactly 5 bytes.");
297+
}
298+
TotalNumHints += NumHints;
299+
}
300+
encodeULEB128(TotalNumHints, buf, 5);
301+
}
302+
303+
void CodeMetaDataSection::finalizeContents() {
304+
finalizeInputSections();
305+
306+
raw_string_ostream os(nameData);
307+
encodeULEB128(name.size(), os);
308+
os << name;
309+
310+
bool firstSection = true;
311+
for (InputChunk *section : inputSections) {
312+
assert(!section->discarded);
313+
payloadSize = alignTo(payloadSize, section->alignment);
314+
if (firstSection) {
315+
section->outSecOff = payloadSize;
316+
payloadSize += section->getSize();
317+
firstSection = false;
318+
} else {
319+
// adjust output offset so that each section write overwrites exactly the
320+
// subsequent section's function hint vector size (which deduplicates)
321+
section->outSecOff = payloadSize - 5;
322+
// payload size should not include the hint vector size, which is deduped
323+
payloadSize += section->getSize() - 5;
324+
}
325+
}
326+
327+
createHeader(payloadSize + nameData.size());
328+
}
329+
274330
uint32_t CustomSection::getNumRelocations() const {
275331
uint32_t count = 0;
276332
for (const InputChunk *inputSect : inputSections)

lld/wasm/OutputSections.h

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

135+
class CodeMetaDataSection : public CustomSection {
136+
public:
137+
CodeMetaDataSection(std::string name, ArrayRef<InputChunk *> inputSections)
138+
: CustomSection(name, inputSections) {}
139+
140+
void writeTo(uint8_t *buf) override;
141+
void finalizeContents() override;
142+
};
143+
135144
} // namespace wasm
136145
} // namespace lld
137146

lld/wasm/Writer.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,17 @@ void Writer::createCustomSections() {
170170
for (auto &pair : customSectionMapping) {
171171
StringRef name = pair.first;
172172
LLVM_DEBUG(dbgs() << "createCustomSection: " << name << "\n");
173-
174-
OutputSection *sec = make<CustomSection>(std::string(name), pair.second);
173+
OutputSection *Sec;
174+
if (name == "metadata.code.branch_hint")
175+
Sec = make<CodeMetaDataSection>(std::string(name), pair.second);
176+
else
177+
Sec = make<CustomSection>(std::string(name), pair.second);
175178
if (ctx.arg.relocatable || ctx.arg.emitRelocs) {
176-
auto *sym = make<OutputSectionSymbol>(sec);
179+
auto *sym = make<OutputSectionSymbol>(Sec);
177180
out.linkingSec->addToSymtab(sym);
178-
sec->sectionSym = sym;
181+
Sec->sectionSym = sym;
179182
}
180-
addSection(sec);
183+
addSection(Sec);
181184
}
182185
}
183186

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "MCTargetDesc/WebAssemblyFixupKinds.h"
1515
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16+
#include "WebAssemblyMCExpr.h"
1617
#include "llvm/MC/MCAsmBackend.h"
1718
#include "llvm/MC/MCAssembler.h"
1819
#include "llvm/MC/MCExpr.h"
@@ -21,6 +22,7 @@
2122
#include "llvm/MC/MCSubtargetInfo.h"
2223
#include "llvm/MC/MCSymbol.h"
2324
#include "llvm/MC/MCWasmObjectWriter.h"
25+
#include "llvm/Support/Casting.h"
2426
#include "llvm/Support/raw_ostream.h"
2527

2628
using namespace llvm;
@@ -46,6 +48,9 @@ class WebAssemblyAsmBackend final : public MCAsmBackend {
4648
std::unique_ptr<MCObjectTargetWriter>
4749
createObjectTargetWriter() const override;
4850

51+
std::pair<bool, bool> relaxLEB128(const MCAssembler &Asm, MCLEBFragment &LF,
52+
int64_t &Value) const override;
53+
4954
bool writeNopData(raw_ostream &OS, uint64_t Count,
5055
const MCSubtargetInfo *STI) const override;
5156
};
@@ -72,6 +77,27 @@ WebAssemblyAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
7277
return Infos[Kind - FirstTargetFixupKind];
7378
}
7479

80+
std::pair<bool, bool>
81+
WebAssemblyAsmBackend::relaxLEB128(const MCAssembler &assembler,
82+
MCLEBFragment &LF, int64_t &Value) const {
83+
const MCExpr &Expr = LF.getValue();
84+
if (Expr.getKind() == MCExpr::ExprKind::SymbolRef) {
85+
const MCSymbolRefExpr &SymExpr = llvm::cast<MCSymbolRefExpr>(Expr);
86+
if (static_cast<WebAssembly::Specifier>(SymExpr.getSpecifier()) ==
87+
WebAssembly::S_DEBUG_REF) {
88+
Value = assembler.getSymbolOffset(SymExpr.getSymbol());
89+
return std::make_pair(true, false);
90+
}
91+
}
92+
// currently, this is only used for leb128 encoded function indices
93+
// that require relocations
94+
LF.getFixups().push_back(
95+
MCFixup::create(0, &Expr, WebAssembly::fixup_uleb128_i32, Expr.getLoc()));
96+
// ensure that the stored placeholder is large enough to hold any 32-bit val
97+
Value = UINT32_MAX;
98+
return std::make_pair(true, false);
99+
}
100+
75101
bool WebAssemblyAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,
76102
const MCSubtargetInfo *STI) const {
77103
for (uint64_t I = 0; I < Count; ++I)

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCExpr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ enum Specifier {
2323
S_TBREL, // Table index relative to __table_base
2424
S_TLSREL, // Memory address relative to __tls_base
2525
S_TYPEINDEX, // Reference to a symbol's type (signature)
26+
S_DEBUG_REF, // Marker placed for generation of metadata.code.* section
2627
};
2728
} // namespace llvm::WebAssembly
2829

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,13 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
9393
case WebAssembly::S_None:
9494
break;
9595
case WebAssembly::S_FUNCINDEX:
96+
if (static_cast<unsigned>(Fixup.getKind()) ==
97+
WebAssembly::fixup_uleb128_i32)
98+
return wasm::R_WASM_FUNCTION_INDEX_LEB;
9699
return wasm::R_WASM_FUNCTION_INDEX_I32;
97100
}
98101

99-
switch (unsigned(Fixup.getKind())) {
102+
switch (static_cast<unsigned>(Fixup.getKind())) {
100103
case WebAssembly::fixup_sleb128_i32:
101104
if (SymA.isFunction())
102105
return wasm::R_WASM_TABLE_INDEX_SLEB;

llvm/lib/Target/WebAssembly/WebAssembly.td

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ def FeatureWideArithmetic :
9090
SubtargetFeature<"wide-arithmetic", "HasWideArithmetic", "true",
9191
"Enable wide-arithmetic instructions">;
9292

93+
def FeatureBranchHinting :
94+
SubtargetFeature<"branch-hinting", "HasBranchHinting", "true",
95+
"Enable branch hints for branch instructions">;
96+
9397
//===----------------------------------------------------------------------===//
9498
// Architectures.
9599
//===----------------------------------------------------------------------===//
@@ -142,7 +146,7 @@ def : ProcessorModel<"bleeding-edge", NoSchedModel,
142146
FeatureMultivalue, FeatureMutableGlobals,
143147
FeatureNontrappingFPToInt, FeatureRelaxedSIMD,
144148
FeatureReferenceTypes, FeatureSIMD128, FeatureSignExt,
145-
FeatureTailCall]>;
149+
FeatureTailCall, FeatureBranchHinting]>;
146150

147151
//===----------------------------------------------------------------------===//
148152
// Target Declaration

0 commit comments

Comments
 (0)