Skip to content

Commit 4cb790f

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 1949536 commit 4cb790f

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
@@ -5276,6 +5276,8 @@ def mtail_call : Flag<["-"], "mtail-call">, Group<m_wasm_Features_Group>;
52765276
def mno_tail_call : Flag<["-"], "mno-tail-call">, Group<m_wasm_Features_Group>;
52775277
def mwide_arithmetic : Flag<["-"], "mwide-arithmetic">, Group<m_wasm_Features_Group>;
52785278
def mno_wide_arithmetic : Flag<["-"], "mno-wide-arithmetic">, Group<m_wasm_Features_Group>;
5279+
def mbranch_hinting : Flag<["-"], "mbranch-hinting">, Group<m_wasm_Features_Group>;
5280+
def mno_branch_hinting : Flag<["-"], "mno-branch-hinting">, Group<m_wasm_Features_Group>;
52795281
def mexec_model_EQ : Joined<["-"], "mexec-model=">, Group<m_wasm_Features_Driver_Group>,
52805282
Values<"command,reactor">,
52815283
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
@@ -72,6 +72,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
7272
bool HasSignExt = false;
7373
bool HasTailCall = false;
7474
bool HasWideArithmetic = false;
75+
bool HasBranchHinting = false;
7576

7677
std::string ABI;
7778

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
@@ -270,6 +270,62 @@ void CustomSection::writeTo(uint8_t *buf) {
270270
section->writeTo(buf);
271271
}
272272

273+
void CodeMetaDataSection::writeTo(uint8_t *buf) {
274+
log("writing " + toString(*this) + " offset=" + Twine(offset) +
275+
" size=" + Twine(getSize()) + " chunks=" + Twine(inputSections.size()));
276+
277+
assert(offset);
278+
buf += offset;
279+
280+
// Write section header
281+
memcpy(buf, header.data(), header.size());
282+
buf += header.size();
283+
memcpy(buf, nameData.data(), nameData.size());
284+
buf += nameData.size();
285+
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+
}
299+
encodeULEB128(TotalNumHints, buf, 5);
300+
}
301+
302+
void CodeMetaDataSection::finalizeContents() {
303+
finalizeInputSections();
304+
305+
raw_string_ostream os(nameData);
306+
encodeULEB128(name.size(), os);
307+
os << name;
308+
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+
}
324+
}
325+
326+
createHeader(payloadSize + nameData.size());
327+
}
328+
273329
uint32_t CustomSection::getNumRelocations() const {
274330
uint32_t count = 0;
275331
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
@@ -165,14 +165,17 @@ void Writer::createCustomSections() {
165165
for (auto &pair : customSectionMapping) {
166166
StringRef name = pair.first;
167167
LLVM_DEBUG(dbgs() << "createCustomSection: " << name << "\n");
168-
169-
OutputSection *sec = make<CustomSection>(std::string(name), pair.second);
168+
OutputSection *Sec;
169+
if (name == "metadata.code.branch_hint")
170+
Sec = make<CodeMetaDataSection>(std::string(name), pair.second);
171+
else
172+
Sec = make<CustomSection>(std::string(name), pair.second);
170173
if (ctx.arg.relocatable || ctx.arg.emitRelocs) {
171-
auto *sym = make<OutputSectionSymbol>(sec);
174+
auto *sym = make<OutputSectionSymbol>(Sec);
172175
out.linkingSec->addToSymtab(sym);
173-
sec->sectionSym = sym;
176+
Sec->sectionSym = sym;
174177
}
175-
addSection(sec);
178+
addSection(Sec);
176179
}
177180
}
178181

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

Lines changed: 25 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 "WebAssemblyMCAsmInfo.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;
@@ -44,6 +46,9 @@ class WebAssemblyAsmBackend final : public MCAsmBackend {
4446
std::unique_ptr<MCObjectTargetWriter>
4547
createObjectTargetWriter() const override;
4648

49+
std::pair<bool, bool> relaxLEB128(MCLEBFragment &LF,
50+
int64_t &Value) const override;
51+
4752
bool writeNopData(raw_ostream &OS, uint64_t Count,
4853
const MCSubtargetInfo *STI) const override;
4954
};
@@ -70,6 +75,26 @@ WebAssemblyAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
7075
return Infos[Kind - FirstTargetFixupKind];
7176
}
7277

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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum Specifier {
3737
S_TBREL, // Table index relative to __table_base
3838
S_TLSREL, // Memory address relative to __tls_base
3939
S_TYPEINDEX, // Reference to a symbol's type (signature)
40+
S_DEBUG_REF, // Marker placed for generation of metadata.code.* section
4041
};
4142
}
4243
} // end namespace llvm

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,16 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
9191
case WebAssembly::S_TYPEINDEX:
9292
return wasm::R_WASM_TYPE_INDEX_LEB;
9393
case WebAssembly::S_None:
94+
case WebAssembly::S_DEBUG_REF:
9495
break;
9596
case WebAssembly::S_FUNCINDEX:
97+
if (static_cast<unsigned>(Fixup.getKind()) ==
98+
WebAssembly::fixup_uleb128_i32)
99+
return wasm::R_WASM_FUNCTION_INDEX_LEB;
96100
return wasm::R_WASM_FUNCTION_INDEX_I32;
97101
}
98102

99-
switch (unsigned(Fixup.getKind())) {
103+
switch (static_cast<unsigned>(Fixup.getKind())) {
100104
case WebAssembly::fixup_sleb128_i32:
101105
if (SymA.isFunction())
102106
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)