https://github.com/Lukasdoe updated 
https://github.com/llvm/llvm-project/pull/146230

From cf1c0ca649741cefdab0eb1538dff58aeee16b0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lukas=20D=C3=B6llerer?= <cont...@lukas-doellerer.de>
Date: Sat, 28 Jun 2025 21:53:09 +0200
Subject: [PATCH] [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.
---
 clang/include/clang/Driver/Options.td         |   2 +
 clang/lib/Basic/Targets/WebAssembly.cpp       |  12 ++
 clang/lib/Basic/Targets/WebAssembly.h         |   1 +
 lld/test/wasm/code-metadata-branch-hints.ll   | 116 ++++++++++++++++
 lld/wasm/OutputSections.cpp                   |  56 ++++++++
 lld/wasm/OutputSections.h                     |   9 ++
 lld/wasm/Writer.cpp                           |  13 +-
 llvm/include/llvm/BinaryFormat/Wasm.h         |  16 +++
 llvm/include/llvm/Object/Wasm.h               |   8 ++
 llvm/include/llvm/ObjectYAML/WasmYAML.h       |  44 ++++++
 llvm/lib/Object/WasmObjectFile.cpp            |  40 ++++++
 llvm/lib/ObjectYAML/WasmYAML.cpp              |  35 +++++
 .../MCTargetDesc/WebAssemblyAsmBackend.cpp    |  25 ++++
 .../MCTargetDesc/WebAssemblyMCAsmInfo.cpp     |   1 +
 .../MCTargetDesc/WebAssemblyMCAsmInfo.h       |   1 +
 .../WebAssemblyWasmObjectWriter.cpp           |   6 +-
 llvm/lib/Target/WebAssembly/WebAssembly.td    |   6 +-
 .../WebAssembly/WebAssemblyAsmPrinter.cpp     |  81 +++++++++++
 .../WebAssembly/WebAssemblyAsmPrinter.h       |  17 +++
 .../WebAssembly/WebAssemblyCFGStackify.cpp    |  22 ++-
 .../WebAssembly/WebAssemblyInstrInfo.td       |   4 +
 .../WebAssemblyMachineFunctionInfo.h          |   2 +
 .../Target/WebAssembly/WebAssemblySubtarget.h |   2 +
 ...branch-hints-custom-high-low-thresholds.ll | 127 ++++++++++++++++++
 llvm/test/MC/WebAssembly/branch-hints.ll      | 122 +++++++++++++++++
 llvm/tools/obj2yaml/wasm2yaml.cpp             |  17 +++
 26 files changed, 777 insertions(+), 8 deletions(-)
 create mode 100644 lld/test/wasm/code-metadata-branch-hints.ll
 create mode 100644 
llvm/test/MC/WebAssembly/branch-hints-custom-high-low-thresholds.ll
 create mode 100644 llvm/test/MC/WebAssembly/branch-hints.ll

diff --git a/clang/include/clang/Driver/Options.td 
b/clang/include/clang/Driver/Options.td
index 9911d752966e3..31274aca2a25b 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -5276,6 +5276,8 @@ def mtail_call : Flag<["-"], "mtail-call">, 
Group<m_wasm_Features_Group>;
 def mno_tail_call : Flag<["-"], "mno-tail-call">, Group<m_wasm_Features_Group>;
 def mwide_arithmetic : Flag<["-"], "mwide-arithmetic">, 
Group<m_wasm_Features_Group>;
 def mno_wide_arithmetic : Flag<["-"], "mno-wide-arithmetic">, 
Group<m_wasm_Features_Group>;
+def mbranch_hinting : Flag<["-"], "mbranch-hinting">, 
Group<m_wasm_Features_Group>;
+def mno_branch_hinting : Flag<["-"], "mno-branch-hinting">, 
Group<m_wasm_Features_Group>;
 def mexec_model_EQ : Joined<["-"], "mexec-model=">, 
Group<m_wasm_Features_Driver_Group>,
                      Values<"command,reactor">,
                      HelpText<"Execution model (WebAssembly only)">,
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp 
b/clang/lib/Basic/Targets/WebAssembly.cpp
index f19c57f1a3a50..14c9d501bc1fa 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -69,6 +69,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) 
const {
       .Case("simd128", SIMDLevel >= SIMD128)
       .Case("tail-call", HasTailCall)
       .Case("wide-arithmetic", HasWideArithmetic)
+      .Case("branch-hinting", HasBranchHinting)
       .Default(false);
 }
 
@@ -116,6 +117,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const 
LangOptions &Opts,
     Builder.defineMacro("__wasm_tail_call__");
   if (HasWideArithmetic)
     Builder.defineMacro("__wasm_wide_arithmetic__");
+  if (HasBranchHinting)
+    Builder.defineMacro("__wasm_branch_hinting__");
 
   Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
   Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
@@ -194,6 +197,7 @@ bool WebAssemblyTargetInfo::initFeatureMap(
     Features["multimemory"] = true;
     Features["tail-call"] = true;
     Features["wide-arithmetic"] = true;
+    Features["branch-hinting"] = true;
     setSIMDLevel(Features, RelaxedSIMD, true);
   };
   if (CPU == "generic") {
@@ -347,6 +351,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
       HasWideArithmetic = false;
       continue;
     }
+    if (Feature == "+branch-hinting") {
+      HasBranchHinting = true;
+      continue;
+    }
+    if (Feature == "-branch-hinting") {
+      HasBranchHinting = false;
+      continue;
+    }
 
     Diags.Report(diag::err_opt_not_valid_with_opt)
         << Feature << "-target-feature";
diff --git a/clang/lib/Basic/Targets/WebAssembly.h 
b/clang/lib/Basic/Targets/WebAssembly.h
index d5aee5c0bd0eb..8b7bc6c43bd17 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -72,6 +72,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public 
TargetInfo {
   bool HasSignExt = false;
   bool HasTailCall = false;
   bool HasWideArithmetic = false;
+  bool HasBranchHinting = false;
 
   std::string ABI;
 
diff --git a/lld/test/wasm/code-metadata-branch-hints.ll 
b/lld/test/wasm/code-metadata-branch-hints.ll
new file mode 100644
index 0000000000000..ec5a908249346
--- /dev/null
+++ b/lld/test/wasm/code-metadata-branch-hints.ll
@@ -0,0 +1,116 @@
+# RUN: rm -rf %t; split-file %s %t
+; RUN: llc -filetype=obj %t/f1.ll -o %t/f1.o -mattr=+branch-hinting
+; RUN: llc -filetype=obj %t/f2.ll -o %t/f2.o -mattr=+branch-hinting
+; RUN: wasm-ld --export-all -o %t.wasm %t/f2.o %t/f1.o
+; RUN: obj2yaml %t.wasm | FileCheck --check-prefixes=CHECK %s
+
+; RUN: llc -filetype=obj %t/f1.ll -o %t/f1.o -mattr=-branch-hinting
+; RUN: llc -filetype=obj %t/f2.ll -o %t/f2.o -mattr=-branch-hinting
+; RUN: wasm-ld --export-all -o %t.wasm %t/f2.o %t/f1.o
+; RUN: obj2yaml %t.wasm | FileCheck --check-prefixes=NCHECK %s
+
+; CHECK:          - Type:            CUSTOM
+; CHECK:            Name:            metadata.code.branch_hint
+; CHECK-NEXT:       Entries:
+; CHECK-NEXT:         - FuncIdx:         1
+; CHECK-NEXT:           Hints:
+; CHECK-NEXT:             - Offset:          7
+; CHECK-NEXT:               Size:            1
+; CHECK-NEXT:               Data:            UNLIKELY
+; CHECK-NEXT:             - Offset:          14
+; CHECK-NEXT:               Size:            1
+; CHECK-NEXT:               Data:            LIKELY
+; CHECK-NEXT:         - FuncIdx:         2
+; CHECK-NEXT:           Hints:
+; CHECK-NEXT:             - Offset:          5
+; CHECK-NEXT:               Size:            1
+; CHECK-NEXT:               Data:            LIKELY
+; CHECK-NEXT:         - FuncIdx:         3
+; CHECK-NEXT:           Hints:
+; CHECK-NEXT:             - Offset:          5
+; CHECK-NEXT:               Size:            1
+; CHECK-NEXT:               Data:            UNLIKELY
+; CHECK-NEXT:         - FuncIdx:         4
+; CHECK-NEXT:           Hints:
+; CHECK-NEXT:             - Offset:          5
+; CHECK-NEXT:               Size:            1
+; CHECK-NEXT:               Data:            LIKELY
+
+; CHECK:         - Type:            CUSTOM
+; CHECK-NEXT:      Name:            name
+; CHECK-NEXT:      FunctionNames:
+; CHECK-NEXT:        - Index:           0
+; CHECK-NEXT:          Name:            __wasm_call_ctors
+; CHECK-NEXT:        - Index:           1
+; CHECK-NEXT:          Name:            test0
+; CHECK-NEXT:        - Index:           2
+; CHECK-NEXT:          Name:            test1
+; CHECK-NEXT:        - Index:           3
+; CHECK-NEXT:          Name:            _start
+; CHECK-NEXT:        - Index:           4
+; CHECK-NEXT:          Name:            test_func1
+
+; CHECK:        - Type:            CUSTOM
+; CHECK:          Name:            target_features
+; CHECK-NEXT:     Features:
+; CHECK-NEXT:       - Prefix:          USED
+; CHECK-NEXT:         Name:            branch-hinting
+
+
+; NCHECK-NOT:         Name:            metadata.code.branch_hint
+; NCHECK-NOT:         Name:            branch-hinting
+
+#--- f1.ll
+define i32 @_start(i32 %a) {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %if.then, label %if.else, !prof !0
+if.then:
+  ret i32 1
+if.else:
+  ret i32 2
+}
+
+define i32 @test_func1(i32 %a) {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %if.then, label %if.else, !prof !1
+if.then:
+  ret i32 1
+if.else:
+  ret i32 2
+}
+
+!0 = !{!"branch_weights", i32 2000, i32 1}
+!1 = !{!"branch_weights", i32 1, i32 2000}
+
+#--- f2.ll
+
+target triple = "wasm32-unknown-unknown"
+
+define i32 @test0(i32 %a) {
+entry:
+  %cmp0 = icmp eq i32 %a, 0
+  br i1 %cmp0, label %if.then, label %ret1, !prof !0
+if.then:
+  %cmp1 = icmp eq i32 %a, 1
+  br i1 %cmp1, label %ret1, label %ret2, !prof !1
+ret1:
+  ret i32 2
+ret2:
+  ret i32 1
+}
+
+define i32 @test1(i32 %a) {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %if.then, label %if.else, !prof !1
+if.then:
+  ret i32 1
+if.else:
+  ret i32 2
+}
+
+; the resulting branch hint is actually reversed, since llvm-br is turned into 
br_unless, inverting branch probs
+!0 = !{!"branch_weights", i32 2000, i32 1}
+!1 = !{!"branch_weights", i32 1, i32 2000}
diff --git a/lld/wasm/OutputSections.cpp b/lld/wasm/OutputSections.cpp
index 8ccd38f7895cb..c4fed3742dc6f 100644
--- a/lld/wasm/OutputSections.cpp
+++ b/lld/wasm/OutputSections.cpp
@@ -270,6 +270,62 @@ void CustomSection::writeTo(uint8_t *buf) {
     section->writeTo(buf);
 }
 
+void CodeMetaDataSection::writeTo(uint8_t *buf) {
+  log("writing " + toString(*this) + " offset=" + Twine(offset) +
+      " size=" + Twine(getSize()) + " chunks=" + Twine(inputSections.size()));
+
+  assert(offset);
+  buf += offset;
+
+  // Write section header
+  memcpy(buf, header.data(), header.size());
+  buf += header.size();
+  memcpy(buf, nameData.data(), nameData.size());
+  buf += nameData.size();
+
+  uint32_t TotalNumHints = 0;
+  for (const InputChunk *section :
+       make_range(inputSections.rbegin(), inputSections.rend())) {
+    section->writeTo(buf);
+    unsigned EncodingSize;
+    uint32_t NumHints =
+        decodeULEB128(buf + section->outSecOff, &EncodingSize, nullptr);
+    if (EncodingSize != 5) {
+      fatal("Unexpected encoding size for function hint vec size in " + name +
+            ": must be exactly 5 bytes.");
+    }
+    TotalNumHints += NumHints;
+  }
+  encodeULEB128(TotalNumHints, buf, 5);
+}
+
+void CodeMetaDataSection::finalizeContents() {
+  finalizeInputSections();
+
+  raw_string_ostream os(nameData);
+  encodeULEB128(name.size(), os);
+  os << name;
+
+  bool firstSection = true;
+  for (InputChunk *section : inputSections) {
+    assert(!section->discarded);
+    payloadSize = alignTo(payloadSize, section->alignment);
+    if (firstSection) {
+      section->outSecOff = payloadSize;
+      payloadSize += section->getSize();
+      firstSection = false;
+    } else {
+      // adjust output offset so that each section write overwrites exactly the
+      // subsequent section's function hint vector size (which deduplicates)
+      section->outSecOff = payloadSize - 5;
+      // payload size should not include the hint vector size, which is deduped
+      payloadSize += section->getSize() - 5;
+    }
+  }
+
+  createHeader(payloadSize + nameData.size());
+}
+
 uint32_t CustomSection::getNumRelocations() const {
   uint32_t count = 0;
   for (const InputChunk *inputSect : inputSections)
diff --git a/lld/wasm/OutputSections.h b/lld/wasm/OutputSections.h
index 4b0329dd16cf2..6580c71ab6f5a 100644
--- a/lld/wasm/OutputSections.h
+++ b/lld/wasm/OutputSections.h
@@ -132,6 +132,15 @@ class CustomSection : public OutputSection {
   std::string nameData;
 };
 
+class CodeMetaDataSection : public CustomSection {
+public:
+  CodeMetaDataSection(std::string name, ArrayRef<InputChunk *> inputSections)
+      : CustomSection(name, inputSections) {}
+
+  void writeTo(uint8_t *buf) override;
+  void finalizeContents() override;
+};
+
 } // namespace wasm
 } // namespace lld
 
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b704677d36c93..e9cfd5eac10db 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -165,14 +165,17 @@ void Writer::createCustomSections() {
   for (auto &pair : customSectionMapping) {
     StringRef name = pair.first;
     LLVM_DEBUG(dbgs() << "createCustomSection: " << name << "\n");
-
-    OutputSection *sec = make<CustomSection>(std::string(name), pair.second);
+    OutputSection *Sec;
+    if (name == "metadata.code.branch_hint")
+      Sec = make<CodeMetaDataSection>(std::string(name), pair.second);
+    else
+      Sec = make<CustomSection>(std::string(name), pair.second);
     if (ctx.arg.relocatable || ctx.arg.emitRelocs) {
-      auto *sym = make<OutputSectionSymbol>(sec);
+      auto *sym = make<OutputSectionSymbol>(Sec);
       out.linkingSec->addToSymtab(sym);
-      sec->sectionSym = sym;
+      Sec->sectionSym = sym;
     }
-    addSection(sec);
+    addSection(Sec);
   }
 }
 
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h 
b/llvm/include/llvm/BinaryFormat/Wasm.h
index 604104ff3cca3..3f6909888f080 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -517,6 +517,22 @@ struct WasmSignature {
   WasmSignature() = default;
 };
 
+template <typename T> struct WasmCodeMetadataItemEntry {
+  uint32_t Offset;
+  uint32_t Size;
+  T Data;
+};
+
+template <typename T> struct WasmCodeMetadataFuncEntry {
+  uint32_t FuncIdx;
+  std::vector<WasmCodeMetadataItemEntry<T>> Hints;
+};
+
+enum class WasmCodeMetadataBranchHint : uint8_t {
+  UNLIKELY = 0x0,
+  LIKELY = 0x1,
+};
+
 // Useful comparison operators
 inline bool operator==(const WasmSignature &LHS, const WasmSignature &RHS) {
   return LHS.State == RHS.State && LHS.Returns == RHS.Returns &&
diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h
index b4ba50778c152..4a292446df957 100644
--- a/llvm/include/llvm/Object/Wasm.h
+++ b/llvm/include/llvm/Object/Wasm.h
@@ -142,6 +142,10 @@ class LLVM_ABI WasmObjectFile : public ObjectFile {
   ArrayRef<wasm::WasmFeatureEntry> getTargetFeatures() const {
     return TargetFeatures;
   }
+  ArrayRef<wasm::WasmCodeMetadataFuncEntry<wasm::WasmCodeMetadataBranchHint>>
+  getBranchHints() const {
+    return BranchHints;
+  }
   ArrayRef<wasm::WasmSignature> types() const { return Signatures; }
   ArrayRef<wasm::WasmImport> imports() const { return Imports; }
   ArrayRef<wasm::WasmTable> tables() const { return Tables; }
@@ -275,12 +279,16 @@ class LLVM_ABI WasmObjectFile : public ObjectFile {
   Error parseProducersSection(ReadContext &Ctx);
   Error parseTargetFeaturesSection(ReadContext &Ctx);
   Error parseRelocSection(StringRef Name, ReadContext &Ctx);
+  Error parseCodeMetadataSection(StringRef Name, ReadContext &Ctx);
+  Error parseBranchHintSection(ReadContext &Ctx);
 
   wasm::WasmObjectHeader Header;
   std::vector<WasmSection> Sections;
   wasm::WasmDylinkInfo DylinkInfo;
   wasm::WasmProducerInfo ProducerInfo;
   std::vector<wasm::WasmFeatureEntry> TargetFeatures;
+  
std::vector<wasm::WasmCodeMetadataFuncEntry<wasm::WasmCodeMetadataBranchHint>>
+      BranchHints;
   std::vector<wasm::WasmSignature> Signatures;
   std::vector<wasm::WasmTable> Tables;
   std::vector<wasm::WasmLimits> Memories;
diff --git a/llvm/include/llvm/ObjectYAML/WasmYAML.h 
b/llvm/include/llvm/ObjectYAML/WasmYAML.h
index b0ab04d1d8ac3..f1bb215c050ac 100644
--- a/llvm/include/llvm/ObjectYAML/WasmYAML.h
+++ b/llvm/include/llvm/ObjectYAML/WasmYAML.h
@@ -39,6 +39,7 @@ LLVM_YAML_STRONG_TYPEDEF(uint32_t, SegmentFlags)
 LLVM_YAML_STRONG_TYPEDEF(uint32_t, LimitFlags)
 LLVM_YAML_STRONG_TYPEDEF(uint32_t, ComdatKind)
 LLVM_YAML_STRONG_TYPEDEF(uint32_t, FeaturePolicyPrefix)
+LLVM_YAML_STRONG_TYPEDEF(uint32_t, BranchHint)
 
 struct FileHeader {
   yaml::Hex32 Version;
@@ -144,6 +145,17 @@ struct FeatureEntry {
   std::string Name;
 };
 
+template <typename T> struct CodeMetadataItemEntry {
+  uint32_t Offset;
+  uint32_t Size;
+  T Data;
+};
+
+template <typename T> struct CodeMetadataFuncEntry {
+  uint32_t FuncIdx;
+  std::vector<CodeMetadataItemEntry<T>> Hints;
+};
+
 struct SegmentInfo {
   uint32_t Index;
   StringRef Name;
@@ -286,6 +298,22 @@ struct TargetFeaturesSection : CustomSection {
   std::vector<FeatureEntry> Features;
 };
 
+template <typename T> struct CodeMetadataSection : CustomSection {
+  CodeMetadataSection(const StringRef Name) : CustomSection(Name) {}
+  std::vector<CodeMetadataFuncEntry<T>> Entries;
+};
+
+struct BranchHintSection : CodeMetadataSection<BranchHint> {
+  BranchHintSection() : CodeMetadataSection("metadata.code.branch_hint") {}
+
+  static bool classof(const Section *S) {
+    auto C = dyn_cast<CustomSection>(S);
+    return C && C->Name == "metadata.code.branch_hint";
+  }
+
+  std::vector<CodeMetadataFuncEntry<BranchHint>> Entries;
+};
+
 struct TypeSection : Section {
   TypeSection() : Section(wasm::WASM_SEC_TYPE) {}
 
@@ -447,6 +475,10 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::ComdatEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Comdat)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkImportInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkExportInfo)
+LLVM_YAML_IS_SEQUENCE_VECTOR(
+    llvm::WasmYAML::CodeMetadataItemEntry<llvm::WasmYAML::BranchHint>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(
+    llvm::WasmYAML::CodeMetadataFuncEntry<llvm::WasmYAML::BranchHint>)
 
 namespace llvm {
 namespace yaml {
@@ -527,10 +559,22 @@ template <> struct 
ScalarEnumerationTraits<WasmYAML::FeaturePolicyPrefix> {
   static void enumeration(IO &IO, WasmYAML::FeaturePolicyPrefix &Prefix);
 };
 
+template <> struct ScalarEnumerationTraits<WasmYAML::BranchHint> {
+  static void enumeration(IO &IO, WasmYAML::BranchHint &BranchHint);
+};
+
 template <> struct MappingTraits<WasmYAML::FeatureEntry> {
   static void mapping(IO &IO, WasmYAML::FeatureEntry &FeatureEntry);
 };
 
+template <typename T> struct MappingTraits<WasmYAML::CodeMetadataFuncEntry<T>> 
{
+  static void mapping(IO &IO, WasmYAML::CodeMetadataFuncEntry<T> &FuncEntry);
+};
+
+template <typename T> struct MappingTraits<WasmYAML::CodeMetadataItemEntry<T>> 
{
+  static void mapping(IO &IO, WasmYAML::CodeMetadataItemEntry<T> &ItemEntry);
+};
+
 template <> struct MappingTraits<WasmYAML::SegmentInfo> {
   static void mapping(IO &IO, WasmYAML::SegmentInfo &SegmentInfo);
 };
diff --git a/llvm/lib/Object/WasmObjectFile.cpp 
b/llvm/lib/Object/WasmObjectFile.cpp
index ee7a3068af91d..964c96712c1f4 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -1030,6 +1030,43 @@ Error 
WasmObjectFile::parseTargetFeaturesSection(ReadContext &Ctx) {
   return Error::success();
 }
 
+Error WasmObjectFile::parseCodeMetadataSection(StringRef Name,
+                                               ReadContext &Ctx) {
+  const auto HintTypeName = Name.substr(14 /* "code_metadata." */);
+  if (HintTypeName == "branch_hint") {
+    return parseBranchHintSection(Ctx);
+  } else {
+    return make_error<GenericBinaryError>(
+        "invalid code metadata section: " + Name, object_error::parse_failed);
+  }
+}
+
+Error WasmObjectFile::parseBranchHintSection(ReadContext &Ctx) {
+  const uint32_t NumFuncs = readVaruint32(Ctx);
+  BranchHints.reserve(NumFuncs);
+  for (size_t i = 0; i < NumFuncs; ++i) {
+    wasm::WasmCodeMetadataFuncEntry<wasm::WasmCodeMetadataBranchHint> 
FuncEntry;
+    FuncEntry.FuncIdx = readVaruint32(Ctx);
+    const uint32_t NumHints = readVaruint32(Ctx);
+    for (size_t j = 0; j < NumHints; ++j) {
+      wasm::WasmCodeMetadataItemEntry<wasm::WasmCodeMetadataBranchHint> Hint;
+      Hint.Offset = readVaruint32(Ctx);
+      Hint.Size = readVaruint32(Ctx);
+      uint8_t Data = readUint8(Ctx);
+      if (Data > 0x1)
+        return make_error<GenericBinaryError>("invalid branch hint data",
+                                              object_error::parse_failed);
+      Hint.Data = static_cast<wasm::WasmCodeMetadataBranchHint>(Data);
+      FuncEntry.Hints.push_back(Hint);
+    }
+    BranchHints.push_back(std::move(FuncEntry));
+  }
+  if (Ctx.Ptr != Ctx.End)
+    return make_error<GenericBinaryError>(
+        "branch hint section ended prematurely", object_error::parse_failed);
+  return Error::success();
+}
+
 Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
   uint32_t SectionIndex = readVaruint32(Ctx);
   if (SectionIndex >= Sections.size())
@@ -1183,6 +1220,9 @@ Error WasmObjectFile::parseCustomSection(WasmSection 
&Sec, ReadContext &Ctx) {
   } else if (Sec.Name == "target_features") {
     if (Error Err = parseTargetFeaturesSection(Ctx))
       return Err;
+  } else if (Sec.Name.starts_with("metadata.code.")) {
+    if (Error Err = parseCodeMetadataSection(Sec.Name, Ctx))
+      return Err;
   } else if (Sec.Name.starts_with("reloc.")) {
     if (Error Err = parseRelocSection(Sec.Name, Ctx))
       return Err;
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index edbcb3c6ead11..1bbb2d1598097 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -94,6 +94,12 @@ static void sectionMapping(IO &IO, 
WasmYAML::TargetFeaturesSection &Section) {
   IO.mapRequired("Features", Section.Features);
 }
 
+static void sectionMapping(IO &IO, WasmYAML::BranchHintSection &Section) {
+  commonSectionMapping(IO, Section);
+  IO.mapRequired("Name", Section.Name);
+  IO.mapRequired("Entries", Section.Entries);
+}
+
 static void sectionMapping(IO &IO, WasmYAML::CustomSection &Section) {
   commonSectionMapping(IO, Section);
   IO.mapRequired("Name", Section.Name);
@@ -202,6 +208,10 @@ void 
MappingTraits<std::unique_ptr<WasmYAML::Section>>::mapping(
       if (!IO.outputting())
         Section.reset(new WasmYAML::TargetFeaturesSection());
       sectionMapping(IO, 
*cast<WasmYAML::TargetFeaturesSection>(Section.get()));
+    } else if (SectionName == "metadata.code.branch_hint") {
+      if (!IO.outputting())
+        Section.reset(new WasmYAML::BranchHintSection());
+      sectionMapping(IO, *cast<WasmYAML::BranchHintSection>(Section.get()));
     } else {
       if (!IO.outputting())
         Section.reset(new WasmYAML::CustomSection(SectionName));
@@ -353,6 +363,31 @@ void MappingTraits<WasmYAML::FeatureEntry>::mapping(
   IO.mapRequired("Name", FeatureEntry.Name);
 }
 
+template <>
+void MappingTraits<WasmYAML::CodeMetadataFuncEntry<WasmYAML::BranchHint>>::
+    mapping(IO &IO,
+            WasmYAML::CodeMetadataFuncEntry<WasmYAML::BranchHint> &FuncEntry) {
+  IO.mapRequired("FuncIdx", FuncEntry.FuncIdx);
+  IO.mapRequired("Hints", FuncEntry.Hints);
+}
+
+template <>
+void MappingTraits<WasmYAML::CodeMetadataItemEntry<WasmYAML::BranchHint>>::
+    mapping(IO &IO,
+            WasmYAML::CodeMetadataItemEntry<WasmYAML::BranchHint> &ItemEntry) {
+  IO.mapRequired("Offset", ItemEntry.Offset);
+  IO.mapRequired("Size", ItemEntry.Size);
+  IO.mapRequired("Data", ItemEntry.Data);
+}
+
+void ScalarEnumerationTraits<WasmYAML::BranchHint>::enumeration(
+    IO &IO, WasmYAML::BranchHint &BranchHint) {
+  IO.enumCase(BranchHint, "UNLIKELY",
+              
static_cast<uint8_t>(wasm::WasmCodeMetadataBranchHint::UNLIKELY));
+  IO.enumCase(BranchHint, "LIKELY",
+              static_cast<uint8_t>(wasm::WasmCodeMetadataBranchHint::LIKELY));
+}
+
 void MappingTraits<WasmYAML::SegmentInfo>::mapping(
     IO &IO, WasmYAML::SegmentInfo &SegmentInfo) {
   IO.mapRequired("Index", SegmentInfo.Index);
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
index 7bc672c069476..218ab188a9d38 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
@@ -13,6 +13,7 @@
 
 #include "MCTargetDesc/WebAssemblyFixupKinds.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblyMCAsmInfo.h"
 #include "llvm/MC/MCAsmBackend.h"
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCExpr.h"
@@ -21,6 +22,7 @@
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/MC/MCSymbol.h"
 #include "llvm/MC/MCWasmObjectWriter.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace llvm;
@@ -44,6 +46,9 @@ class WebAssemblyAsmBackend final : public MCAsmBackend {
   std::unique_ptr<MCObjectTargetWriter>
   createObjectTargetWriter() const override;
 
+  std::pair<bool, bool> relaxLEB128(MCLEBFragment &LF,
+                                    int64_t &Value) const override;
+
   bool writeNopData(raw_ostream &OS, uint64_t Count,
                     const MCSubtargetInfo *STI) const override;
 };
@@ -70,6 +75,26 @@ WebAssemblyAsmBackend::getFixupKindInfo(MCFixupKind Kind) 
const {
   return Infos[Kind - FirstTargetFixupKind];
 }
 
+std::pair<bool, bool> WebAssemblyAsmBackend::relaxLEB128(MCLEBFragment &LF,
+                                                         int64_t &Value) const 
{
+  const MCExpr &Expr = LF.getValue();
+  if (Expr.getKind() == MCExpr::ExprKind::SymbolRef) {
+    const MCSymbolRefExpr &SymExpr = llvm::cast<MCSymbolRefExpr>(Expr);
+    if (static_cast<WebAssembly::Specifier>(SymExpr.getSpecifier()) ==
+        WebAssembly::S_DEBUG_REF) {
+      Value = Asm->getSymbolOffset(SymExpr.getSymbol());
+      return std::make_pair(true, false);
+    }
+  }
+  // currently, this is only used for leb128 encoded function indices
+  // that require relocations
+  LF.getFixups().push_back(
+      MCFixup::create(0, &Expr, WebAssembly::fixup_uleb128_i32, 
Expr.getLoc()));
+  // ensure that the stored placeholder is large enough to hold any 32-bit val
+  Value = UINT32_MAX;
+  return std::make_pair(true, false);
+}
+
 bool WebAssemblyAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,
                                          const MCSubtargetInfo *STI) const {
   for (uint64_t I = 0; I < Count; ++I)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
index 3987b086195a4..bc11fc6b00d89 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
@@ -29,6 +29,7 @@ const MCAsmInfo::AtSpecifier atSpecifiers[] = {
     {WebAssembly::S_GOT, "GOT"},
     {WebAssembly::S_GOT_TLS, "GOT@TLS"},
     {WebAssembly::S_FUNCINDEX, "FUNCINDEX"},
+    {WebAssembly::S_DEBUG_REF, "DEBUGREF"},
 };
 
 WebAssemblyMCAsmInfo::~WebAssemblyMCAsmInfo() = default; // anchor.
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.h 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.h
index 0b3778b36d6ac..bfb5c59b1b80c 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.h
@@ -37,6 +37,7 @@ enum Specifier {
   S_TBREL,     // Table index relative to __table_base
   S_TLSREL,    // Memory address relative to __tls_base
   S_TYPEINDEX, // Reference to a symbol's type (signature)
+  S_DEBUG_REF, // Marker placed for generation of metadata.code.* section
 };
 }
 } // end namespace llvm
diff --git 
a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
index 2cf4bec077385..b910f1f1529e4 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
@@ -91,12 +91,16 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
   case WebAssembly::S_TYPEINDEX:
     return wasm::R_WASM_TYPE_INDEX_LEB;
   case WebAssembly::S_None:
+  case WebAssembly::S_DEBUG_REF:
     break;
   case WebAssembly::S_FUNCINDEX:
+    if (static_cast<unsigned>(Fixup.getKind()) ==
+        WebAssembly::fixup_uleb128_i32)
+      return wasm::R_WASM_FUNCTION_INDEX_LEB;
     return wasm::R_WASM_FUNCTION_INDEX_I32;
   }
 
-  switch (unsigned(Fixup.getKind())) {
+  switch (static_cast<unsigned>(Fixup.getKind())) {
   case WebAssembly::fixup_sleb128_i32:
     if (SymA.isFunction())
       return wasm::R_WASM_TABLE_INDEX_SLEB;
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td 
b/llvm/lib/Target/WebAssembly/WebAssembly.td
index 13603f8181198..ec3889e2037e4 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.td
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.td
@@ -90,6 +90,10 @@ def FeatureWideArithmetic :
       SubtargetFeature<"wide-arithmetic", "HasWideArithmetic", "true",
                        "Enable wide-arithmetic instructions">;
 
+def FeatureBranchHinting :
+      SubtargetFeature<"branch-hinting", "HasBranchHinting", "true",
+                       "Enable branch hints for branch instructions">;
+
 
//===----------------------------------------------------------------------===//
 // Architectures.
 
//===----------------------------------------------------------------------===//
@@ -142,7 +146,7 @@ def : ProcessorModel<"bleeding-edge", NoSchedModel,
                        FeatureMultivalue, FeatureMutableGlobals,
                        FeatureNontrappingFPToInt, FeatureRelaxedSIMD,
                        FeatureReferenceTypes, FeatureSIMD128, FeatureSignExt,
-                       FeatureTailCall]>;
+                       FeatureTailCall, FeatureBranchHinting]>;
 
 
//===----------------------------------------------------------------------===//
 // Target Declaration
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 44a19e4baaf62..0abc18a61585b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -55,6 +55,17 @@ using namespace llvm;
 #define DEBUG_TYPE "asm-printer"
 
 extern cl::opt<bool> WasmKeepRegisters;
+// values are divided by 1<<31 to calculate the probability
+static cl::opt<float>
+    WasmHighBranchProb("wasm-branch-prob-high", cl::Hidden,
+                       cl::desc("lowest branch probability to not be annotated 
"
+                                "as likely taken (range [0.0-1.0])"),
+                       cl::init(0.5f));
+static cl::opt<float>
+    WasmLowBranchProb("wasm-branch-prob-low", cl::Hidden,
+                      cl::desc("highest branch probability to be annotated as "
+                               "unlikely taken (range [0.0-1.0])"),
+                      cl::init(0.5f));
 
 
//===----------------------------------------------------------------------===//
 // Helpers.
@@ -442,6 +453,40 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
   EmitProducerInfo(M);
   EmitTargetFeatures(M);
   EmitFunctionAttributes(M);
+
+  // Subtarget may be null if no functions have been defined in file
+  if (Subtarget && Subtarget->hasBranchHinting())
+    emitBranchHintSection();
+}
+
+void WebAssemblyAsmPrinter::emitBranchHintSection() const {
+  MCSectionWasm *BranchHintsSection = OutContext.getWasmSection(
+      "metadata.code.branch_hint", SectionKind::getMetadata());
+  OutStreamer->pushSection();
+  OutStreamer->switchSection(BranchHintsSection);
+  const uint32_t NumFunctionHints =
+      std::count_if(BranchHints.begin(), BranchHints.end(),
+                    [](const auto &BHR) { return !BHR.Hints.empty(); });
+  OutStreamer->emitULEB128IntValue(NumFunctionHints, 5);
+  for (const auto &BHR : BranchHints) {
+    if (BHR.Hints.empty())
+      continue;
+    // emit relocatable function index for the function symbol
+    OutStreamer->emitULEB128Value(MCSymbolRefExpr::create(
+        BHR.FuncSym, WebAssembly::S_FUNCINDEX, OutContext));
+    // emit the number of hints for this function (is constant -> does not need
+    // handling by target streamer for reloc)
+    OutStreamer->emitULEB128IntValue(BHR.Hints.size());
+    for (const auto &[instrSym, hint] : BHR.Hints) {
+      assert(hint == 0 || hint == 1);
+      // offset from function start
+      OutStreamer->emitULEB128Value(MCSymbolRefExpr::create(
+          instrSym, WebAssembly::S_DEBUG_REF, OutContext));
+      OutStreamer->emitULEB128IntValue(1); // hint size
+      OutStreamer->emitULEB128IntValue(hint);
+    }
+  }
+  OutStreamer->popSection();
 }
 
 void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) {
@@ -697,6 +742,42 @@ void WebAssemblyAsmPrinter::emitInstruction(const 
MachineInstr *MI) {
     WebAssemblyMCInstLower MCInstLowering(OutContext, *this);
     MCInst TmpInst;
     MCInstLowering.lower(MI, TmpInst);
+    if (Subtarget->hasBranchHinting() && MFI) {
+      if (const auto Prob = MFI->BranchProbabilities.find(MI);
+          Prob != MFI->BranchProbabilities.end()) {
+        const float ThresholdProbLow = WasmLowBranchProb.getValue();
+        const float ThresholdProbHigh = WasmHighBranchProb.getValue();
+        assert(ThresholdProbLow >= 0.0f && ThresholdProbLow <= 1.0f &&
+               ThresholdProbHigh >= 0.0f && ThresholdProbHigh <= 1.0f &&
+               "Branch probability thresholds must be in range [0.0-1.0]");
+
+        MCSymbol *BrIfSym = OutContext.createTempSymbol();
+        OutStreamer->emitLabel(BrIfSym);
+        constexpr uint8_t HintLikely = 0x01;
+        constexpr uint8_t HintUnlikely = 0x00;
+        const uint32_t D = BranchProbability::getOne().getDenominator();
+        uint8_t HintValue;
+        if (Prob->getSecond() >
+            BranchProbability::getRaw(ThresholdProbHigh * D))
+          HintValue = HintLikely;
+        else if (Prob->getSecond() <=
+                 BranchProbability::getRaw(ThresholdProbLow * D))
+          HintValue = HintUnlikely;
+        else
+          goto emit; // Don't emit branch hint between thresholds
+
+        // we know that we only emit branch hints for internal functions,
+        // therefore we can directly cast and don't need getMCSymbolForFunction
+        MCSymbol *FuncSym = cast<MCSymbolWasm>(getSymbol(&MF->getFunction()));
+        const uint32_t LocalFuncIdx = MF->getFunctionNumber();
+        if (BranchHints.size() <= LocalFuncIdx) {
+          BranchHints.resize(LocalFuncIdx + 1);
+          BranchHints[LocalFuncIdx].FuncSym = FuncSym;
+        }
+        BranchHints[LocalFuncIdx].Hints.emplace_back(BrIfSym, HintValue);
+      }
+    }
+  emit:
     EmitToStreamer(*OutStreamer, TmpInst);
     break;
   }
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h 
b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h
index 46063bbe0fba1..1876840248053 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h
@@ -12,12 +12,19 @@
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "WebAssemblySubtarget.h"
 #include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/Module.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/Target/TargetMachine.h"
 
 namespace llvm {
 class WebAssemblyTargetStreamer;
 
+struct BranchHintRecord {
+  MCSymbol *FuncSym;
+  SmallVector<std::pair<MCSymbol *, uint8_t>, 0> Hints;
+};
+
 class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : public AsmPrinter {
 public:
   static char ID;
@@ -28,6 +35,9 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : 
public AsmPrinter {
   WebAssemblyFunctionInfo *MFI;
   bool signaturesEmitted = false;
 
+  // vec idx == local func_idx
+  SmallVector<BranchHintRecord> BranchHints;
+
 public:
   explicit WebAssemblyAsmPrinter(TargetMachine &TM,
                                  std::unique_ptr<MCStreamer> Streamer)
@@ -48,6 +58,12 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : 
public AsmPrinter {
     Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
     MRI = &MF.getRegInfo();
     MFI = MF.getInfo<WebAssemblyFunctionInfo>();
+
+    if (Subtarget->hasBranchHinting()) {
+      const uint32_t LocalFuncIdx = MF.getFunctionNumber();
+      BranchHints.resize(MMI->getModule()->getFunctionList().size());
+      BranchHints[LocalFuncIdx].FuncSym = getSymbol(&MF.getFunction());
+    }
     return AsmPrinter::runOnMachineFunction(MF);
   }
 
@@ -59,6 +75,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : 
public AsmPrinter {
   void EmitProducerInfo(Module &M);
   void EmitTargetFeatures(Module &M);
   void EmitFunctionAttributes(Module &M);
+  void emitBranchHintSection() const;
   void emitSymbolType(const MCSymbolWasm *Sym);
   void emitGlobalVariable(const GlobalVariable *GV) override;
   void emitJumpTableInfo() override;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
index 640be5fe8e8c9..b24838cf8444d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
@@ -31,6 +31,7 @@
 #include "WebAssemblyUtilities.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/BinaryFormat/Wasm.h"
+#include "llvm/CodeGen/MachineBranchProbabilityInfo.h"
 #include "llvm/CodeGen/MachineDominators.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineLoopInfo.h"
@@ -48,6 +49,7 @@ STATISTIC(NumCatchUnwindMismatches, "Number of catch unwind 
mismatches found");
 namespace {
 class WebAssemblyCFGStackify final : public MachineFunctionPass {
   MachineDominatorTree *MDT;
+  MachineBranchProbabilityInfo *MBPI;
 
   StringRef getPassName() const override { return "WebAssembly CFG Stackify"; }
 
@@ -55,6 +57,7 @@ class WebAssemblyCFGStackify final : public 
MachineFunctionPass {
     AU.addRequired<MachineDominatorTreeWrapperPass>();
     AU.addRequired<MachineLoopInfoWrapperPass>();
     AU.addRequired<WebAssemblyExceptionInfo>();
+    AU.addRequired<MachineBranchProbabilityInfoWrapperPass>();
     MachineFunctionPass::getAnalysisUsage(AU);
   }
 
@@ -2562,8 +2565,9 @@ void 
WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
           MO = MachineOperand::CreateImm(getDelegateDepth(Stack, MO.getMBB()));
         else if (MI.getOpcode() == WebAssembly::RETHROW)
           MO = MachineOperand::CreateImm(getRethrowDepth(Stack, MO.getMBB()));
-        else
+        else {
           MO = MachineOperand::CreateImm(getBranchDepth(Stack, MO.getMBB()));
+        }
       }
       MI.addOperand(MF, MO);
     }
@@ -2608,6 +2612,21 @@ void 
WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
         Stack.push_back(std::make_pair(&MBB, &MI));
         break;
 
+      case WebAssembly::BR_IF: {
+        // this is the last place where we can easily calculate the branch
+        // probabilities. we do not emit scf-ifs, therefore, only br_ifs have
+        // to be annotated with branch probabilities.
+        if (MF.getSubtarget<WebAssemblySubtarget>().hasBranchHinting() &&
+            MI.getParent()->hasSuccessorProbabilities()) {
+          const auto Prob = MBPI->getEdgeProbability(
+              MI.getParent(), MI.operands().begin()->getMBB());
+          WebAssemblyFunctionInfo *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
+          assert(!MFI->BranchProbabilities.contains(&MI));
+          MFI->BranchProbabilities[&MI] = Prob;
+        }
+        RewriteOperands(MI);
+        break;
+      }
       default:
         if (MI.isTerminator())
           RewriteOperands(MI);
@@ -2639,6 +2658,7 @@ bool 
WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) {
                     << MF.getName() << '\n');
   const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo();
   MDT = &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+  MBPI = &getAnalysis<MachineBranchProbabilityInfoWrapperPass>().getMBPI();
 
   releaseMemory();
 
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td 
b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index b5e723e2a48d3..f1d4a62535060 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -96,6 +96,10 @@ def HasWideArithmetic :
     Predicate<"Subtarget->hasWideArithmetic()">,
     AssemblerPredicate<(all_of FeatureWideArithmetic), "wide-arithmetic">;
 
+def HasBranchHinting :
+    Predicate<"Subtarget->hasBranchHinting()">,
+    AssemblerPredicate<(all_of FeatureBranchHinting), "branch-hinting">;
+
 
//===----------------------------------------------------------------------===//
 // WebAssembly-specific DAG Node Types.
 
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h 
b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
index 40ae4aef1d7f2..343168b570bef 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
@@ -153,6 +153,8 @@ class WebAssemblyFunctionInfo final : public 
MachineFunctionInfo {
 
   bool isCFGStackified() const { return CFGStackified; }
   void setCFGStackified(bool Value = true) { CFGStackified = Value; }
+
+  DenseMap<const MachineInstr *, BranchProbability> BranchProbabilities;
 };
 
 void computeLegalValueVTs(const WebAssemblyTargetLowering &TLI,
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h 
b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
index 591ce25611e3e..96a24a1d40ef7 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
@@ -54,6 +54,7 @@ class WebAssemblySubtarget final : public 
WebAssemblyGenSubtargetInfo {
   bool HasSignExt = false;
   bool HasTailCall = false;
   bool HasWideArithmetic = false;
+  bool HasBranchHinting = false;
 
   /// What processor and OS we're targeting.
   Triple TargetTriple;
@@ -112,6 +113,7 @@ class WebAssemblySubtarget final : public 
WebAssemblyGenSubtargetInfo {
   bool hasSIMD128() const { return SIMDLevel >= SIMD128; }
   bool hasTailCall() const { return HasTailCall; }
   bool hasWideArithmetic() const { return HasWideArithmetic; }
+  bool hasBranchHinting() const { return HasBranchHinting; }
 
   /// Parses features string setting specified subtarget options. Definition of
   /// function is auto generated by tblgen.
diff --git 
a/llvm/test/MC/WebAssembly/branch-hints-custom-high-low-thresholds.ll 
b/llvm/test/MC/WebAssembly/branch-hints-custom-high-low-thresholds.ll
new file mode 100644
index 0000000000000..9125de49e941e
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/branch-hints-custom-high-low-thresholds.ll
@@ -0,0 +1,127 @@
+; RUN: llc -mcpu=mvp -filetype=obj %s -mattr=+branch-hinting 
-wasm-branch-prob-high=.5 -wasm-branch-prob-low=0.5 -o - | obj2yaml | FileCheck 
--check-prefixes=C1 %s
+; RUN: llc -mcpu=mvp -filetype=obj %s -mattr=+branch-hinting 
-wasm-branch-prob-high=.76 -wasm-branch-prob-low=0 -o - | obj2yaml | FileCheck 
--check-prefixes=C2 %s
+; RUN: llc -mcpu=mvp -filetype=obj %s -mattr=+branch-hinting 
-wasm-branch-prob-high=.75 -wasm-branch-prob-low=0 -o - | obj2yaml | FileCheck 
--check-prefixes=C3 %s
+
+; This test checks that branch weight metadata (!prof) is correctly translated 
to webassembly branch hints
+; We set the prob-thresholds so that "likely" branches are only emitted if 
prob > 75% and "unlikely" branches
+; if prob <= 0%.
+
+; C1:   - Type:            CUSTOM
+; C1-NEXT:     Relocations:
+; C1-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; C1-NEXT:         Index:           0
+; C1-NEXT:         Offset:          0x5
+; C1-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; C1-NEXT:         Index:           1
+; C1-NEXT:         Offset:          0xE
+; C1-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; C1-NEXT:         Index:           2
+; C1-NEXT:         Offset:          0x17
+; C1-NEXT:     Name:            metadata.code.branch_hint
+; C1-NEXT:     Entries:
+; C1-NEXT:       - FuncIdx:         0
+; C1-NEXT:         Hints:
+; C1-NEXT:           - Offset:          5
+; C1-NEXT:             Size:            1
+; C1-NEXT:             Data:            LIKELY
+; C1-NEXT:       - FuncIdx:         1
+; C1-NEXT:         Hints:
+; C1-NEXT:           - Offset:          5
+; C1-NEXT:             Size:            1
+; C1-NEXT:             Data:            UNLIKELY
+; C1-NEXT:       - FuncIdx:         2
+; C1-NEXT:         Hints:
+; C1-NEXT:           - Offset:          5
+; C1-NEXT:             Size:            1
+; C1-NEXT:             Data:            UNLIKELY
+
+; C2:   - Type:            CUSTOM
+; C2-NEXT:     Relocations:
+; C2-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; C2-NEXT:         Index:           2
+; C2-NEXT:         Offset:          0x5
+; C2-NEXT:     Name:            metadata.code.branch_hint
+; C2-NEXT:     Entries:
+; C2-NEXT:       - FuncIdx:         2
+; C2-NEXT:         Hints:
+; C2-NEXT:           - Offset:          5
+; C2-NEXT:             Size:            1
+; C2-NEXT:             Data:            UNLIKELY
+
+; C3:   - Type:            CUSTOM
+; C3-NEXT:     Relocations:
+; C3-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; C3-NEXT:         Index:           0
+; C3-NEXT:         Offset:          0x5
+; C3-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; C3-NEXT:         Index:           2
+; C3-NEXT:         Offset:          0xE
+; C3-NEXT:     Name:            metadata.code.branch_hint
+; C3-NEXT:     Entries:
+; C3-NEXT:       - FuncIdx:         0
+; C3-NEXT:         Hints:
+; C3-NEXT:           - Offset:          5
+; C3-NEXT:             Size:            1
+; C3-NEXT:             Data:            LIKELY
+; C3-NEXT:       - FuncIdx:         2
+; C3-NEXT:         Hints:
+; C3-NEXT:           - Offset:          5
+; C3-NEXT:             Size:            1
+; C3-NEXT:             Data:            UNLIKELY
+
+; CHECK:   - Type:            CUSTOM
+; CHECK-NEXT:     Name:            linking
+; CHECK-NEXT:     Version:         2
+; CHECK-NEXT:     SymbolTable:
+; CHECK-NEXT:       - Index:           0
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Name:            test0
+; CHECK-NEXT:         Flags:           [  ]
+; CHECK-NEXT:         Function:        0
+; CHECK-NEXT:       - Index:           1
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Name:            test1
+; CHECK-NEXT:         Flags:           [  ]
+; CHECK-NEXT:         Function:        1
+; CHECK-NEXT:       - Index:           2
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Name:            test2
+; CHECK-NEXT:         Flags:           [  ]
+; CHECK-NEXT:         Function:        2
+
+target triple = "wasm32-unknown-unknown"
+
+define i32 @test0(i32 %a) {
+entry:
+  %cmp0 = icmp eq i32 %a, 0
+  br i1 %cmp0, label %if_then, label %if_else, !prof !0
+if_then:
+  ret i32 1
+if_else:
+  ret i32 0
+}
+
+define i32 @test1(i32 %a) {
+entry:
+  %cmp0 = icmp eq i32 %a, 0
+  br i1 %cmp0, label %if_then, label %if_else, !prof !1
+if_then:
+  ret i32 1
+if_else:
+  ret i32 0
+}
+
+define i32 @test2(i32 %a) {
+entry:
+  %cmp0 = icmp eq i32 %a, 0
+  br i1 %cmp0, label %if_then, label %if_else, !prof !2
+if_then:
+  ret i32 1
+if_else:
+  ret i32 0
+}
+
+; the resulting branch hint is actually reversed, since llvm-br is turned into 
br_unless, inverting branch probs
+!0 = !{!"branch_weights", !"expected", i32 100, i32 310} ; prob 75.61%
+!1 = !{!"branch_weights", i32 1, i32 1}                  ; prob == 50% (no 
hint)
+!2 = !{!"branch_weights", i32 1, i32 0}                  ; prob == 0% 
(unlikely hint)
diff --git a/llvm/test/MC/WebAssembly/branch-hints.ll 
b/llvm/test/MC/WebAssembly/branch-hints.ll
new file mode 100644
index 0000000000000..2af080ad2b223
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/branch-hints.ll
@@ -0,0 +1,122 @@
+; RUN: llc -mcpu=mvp -filetype=asm %s -mattr=+branch-hinting -o - | FileCheck 
--check-prefixes=ASM-CHECK %s
+; RUN: llc -mcpu=mvp -filetype=asm %s -mattr=-branch-hinting -o - | FileCheck 
--check-prefixes=ASM-NCHECK %s
+; RUN: llc -mcpu=mvp -filetype=asm %s -o - | FileCheck 
--check-prefixes=ASM-NCHECK %s
+; RUN: llc -mcpu=mvp -filetype=obj %s -mattr=+branch-hinting -o - | obj2yaml | 
FileCheck --check-prefixes=YAML-CHECK %s
+; RUN: llc -mcpu=mvp -filetype=obj %s -mattr=-branch-hinting -o - | obj2yaml | 
FileCheck --check-prefixes=YAML-NCHECK %s
+; RUN: llc -mcpu=mvp -filetype=obj %s -o - | obj2yaml | FileCheck 
--check-prefixes=YAML-NCHECK %s
+
+; This test checks that branch weight metadata (!prof) is correctly lowered to
+; the WebAssembly branch hint custom section.
+
+; ASM-CHECK:        test_unlikely_likely_branch:            # 
@test_unlikely_likely_branch
+; ASM-CHECK:        .Ltmp0:
+; ASM-CHECK-NEXT:     br_if           0                               # 0: 
down to label1
+; ASM-CHECK:        .Ltmp1:
+; ASM-CHECK-NEXT:     br_if           1                               # 1: 
down to label0
+
+; ASM-CHECK:        test_likely_branch:                     # 
@test_likely_branch
+; ASM-CHECK:        .Ltmp2:
+; ASM-CHECK-NEXT:     br_if           0                               # 0: 
down to label2
+
+; ASM-CHECK:        .section        .custom_section.target_features,"",@
+; ASM-CHECK-NEXT:   .int8   1
+; ASM-CHECK-NEXT:   .int8   43
+; ASM-CHECK-NEXT:   .int8   14
+; ASM-CHECK-NEXT:   .ascii  "branch-hinting"
+
+; ASM-CHECK:        .section        metadata.code.branch_hint,"",@
+; ASM-CHECK-NEXT:   .asciz  "\202\200\200\200"
+; ASM-CHECK-NEXT:   .uleb128 test_unlikely_likely_branch@FUNCINDEX
+; ASM-CHECK-NEXT:   .int8   2
+; ASM-CHECK-NEXT:   .uleb128 .Ltmp0@DEBUGREF
+; ASM-CHECK-NEXT:   .int8   1
+; ASM-CHECK-NEXT:   .int8   0
+; ASM-CHECK-NEXT:   .uleb128 .Ltmp1@DEBUGREF
+; ASM-CHECK-NEXT:   .int8   1
+; ASM-CHECK-NEXT:   .int8   1
+; ASM-CHECK-NEXT:   .uleb128 test_likely_branch@FUNCINDEX
+; ASM-CHECK-NEXT:   .int8   1
+; ASM-CHECK-NEXT:   .uleb128 .Ltmp2@DEBUGREF
+; ASM-CHECK-NEXT:   .int8   1
+; ASM-CHECK-NEXT:   .int8   1
+
+; ASM-NCHECK-NOT:   .ascii      "branch-hinting"
+; ASM-NCHECK-NOT:   .section        metadata.code.branch_hint,"",@
+
+; YAML-CHECK:        - Type:            CUSTOM
+; YAML-CHECK-NEXT:     Relocations:
+; YAML-CHECK-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; YAML-CHECK-NEXT:         Index:           0
+; YAML-CHECK-NEXT:         Offset:          0x5
+; YAML-CHECK-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+; YAML-CHECK-NEXT:         Index:           1
+; YAML-CHECK-NEXT:         Offset:          0x11
+; YAML-CHECK-NEXT:     Name:            metadata.code.branch_hint
+; YAML-CHECK-NEXT:     Entries:
+; YAML-CHECK-NEXT:       - FuncIdx:         0
+; YAML-CHECK-NEXT:         Hints:
+; YAML-CHECK-NEXT:           - Offset:          7
+; YAML-CHECK-NEXT:             Size:            1
+; YAML-CHECK-NEXT:             Data:            UNLIKELY
+; YAML-CHECK-NEXT:           - Offset:          14
+; YAML-CHECK-NEXT:             Size:            1
+; YAML-CHECK-NEXT:             Data:            LIKELY
+; YAML-CHECK-NEXT:       - FuncIdx:         1
+; YAML-CHECK-NEXT:         Hints:
+; YAML-CHECK-NEXT:           - Offset:          5
+; YAML-CHECK-NEXT:             Size:            1
+; YAML-CHECK-NEXT:             Data:            LIKELY
+
+; YAML-CHECK:        - Type:            CUSTOM
+; YAML-CHECK-NEXT:     Name:            linking
+; YAML-CHECK-NEXT:     Version:         2
+; YAML-CHECK-NEXT:     SymbolTable:
+; YAML-CHECK-NEXT:       - Index:           0
+; YAML-CHECK-NEXT:         Kind:            FUNCTION
+; YAML-CHECK-NEXT:         Name:            test_unlikely_likely_branch
+; YAML-CHECK-NEXT:         Flags:           [  ]
+; YAML-CHECK-NEXT:         Function:        0
+; YAML-CHECK-NEXT:       - Index:           1
+; YAML-CHECK-NEXT:         Kind:            FUNCTION
+; YAML-CHECK-NEXT:         Name:            test_likely_branch
+; YAML-CHECK-NEXT:         Flags:           [  ]
+; YAML-CHECK-NEXT:         Function:        1
+
+; YAML-CHECK:        - Type:            CUSTOM
+; YAML-CHECK-NEXT:     Name:            target_features
+; YAML-CHECK-NEXT:     Features:
+; YAML-CHECK-NEXT:       - Prefix:          USED
+; YAML-CHECK-NEXT:         Name:            branch-hinting
+
+; YAML-NCHECK-NOT:     Name:            metadata.code.branch_hint
+; YAML-NCHECK-NOT:     Name:            branch-hinting
+
+target triple = "wasm32-unknown-unknown"
+
+define i32 @test_unlikely_likely_branch(i32 %a) {
+entry:
+  %cmp0 = icmp eq i32 %a, 0
+  ; This metadata hints that the true branch is overwhelmingly likely.
+  br i1 %cmp0, label %if.then, label %ret1, !prof !0
+if.then:
+  %cmp1 = icmp eq i32 %a, 1
+  br i1 %cmp1, label %ret1, label %ret2, !prof !1
+ret1:
+  ret i32 2
+ret2:
+  ret i32 1
+}
+
+define i32 @test_likely_branch(i32 %a) {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %if.then, label %if.else, !prof !1
+if.then:
+  ret i32 1
+if.else:
+  ret i32 2
+}
+
+; the resulting branch hint is actually reversed, since llvm-br is turned into 
br_unless, inverting branch probs
+!0 = !{!"branch_weights", i32 2000, i32 1}
+!1 = !{!"branch_weights", i32 1, i32 2000}
diff --git a/llvm/tools/obj2yaml/wasm2yaml.cpp 
b/llvm/tools/obj2yaml/wasm2yaml.cpp
index 7b504faa5ae4e..bcc67db3ef967 100644
--- a/llvm/tools/obj2yaml/wasm2yaml.cpp
+++ b/llvm/tools/obj2yaml/wasm2yaml.cpp
@@ -189,6 +189,23 @@ WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
       TargetFeaturesSec->Features.push_back(Feature);
     }
     CustomSec = std::move(TargetFeaturesSec);
+  } else if (WasmSec.Name == "metadata.code.branch_hint") {
+    std::unique_ptr<WasmYAML::BranchHintSection> BranchHintSec =
+        std::make_unique<WasmYAML::BranchHintSection>();
+    for (auto &E : Obj.getBranchHints()) {
+      WasmYAML::CodeMetadataFuncEntry<WasmYAML::BranchHint> FuncEntry;
+      FuncEntry.FuncIdx = E.FuncIdx;
+      for (const auto &[Offset, Size, Data] : E.Hints) {
+        WasmYAML::CodeMetadataItemEntry<WasmYAML::BranchHint> ItemEntry;
+        ItemEntry.Offset = Offset;
+        ItemEntry.Size = Size;
+        ItemEntry.Data =
+            static_cast<WasmYAML::BranchHint>(static_cast<uint8_t>(Data));
+        FuncEntry.Hints.push_back(ItemEntry);
+      }
+      BranchHintSec->Entries.push_back(std::move(FuncEntry));
+    }
+    CustomSec = std::move(BranchHintSec);
   } else {
     CustomSec = std::make_unique<WasmYAML::CustomSection>(WasmSec.Name);
   }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to