https://github.com/trcrsired updated 
https://github.com/llvm/llvm-project/pull/162972

>From b2309c93f36530fa497093744e4c984dd05cbee4 Mon Sep 17 00:00:00 2001
From: trcrsired <[email protected]>
Date: Sun, 20 Sep 2026 04:03:03 +0800
Subject: [PATCH] [WebAssembly] Memory tagging: squashed mt-2 work onto main

Squashed ~140 local commits (WebAssembly stack tagging / memtag
intrinsics, clang builtins, and related work) into a single commit
on top of llvm-project main.
---
 .../clang/Basic/BuiltinsWebAssembly.def       |  27 ++
 .../CodeGen/TargetBuiltins/WebAssembly.cpp    | 201 ++++++++++
 clang/lib/Driver/SanitizerArgs.cpp            |   2 +-
 clang/lib/Driver/ToolChain.cpp                |   2 +-
 clang/lib/Driver/ToolChains/CommonArgs.cpp    |  28 +-
 .../CodeGen/WebAssembly/builtins-memtag.c     | 200 ++++++++++
 llvm/include/llvm/IR/IntrinsicsWebAssembly.td |  79 ++++
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp    |  13 +-
 llvm/lib/MC/MCWasmStreamer.cpp                |   6 +
 llvm/lib/Target/WebAssembly/CMakeLists.txt    |   2 +
 llvm/lib/Target/WebAssembly/WebAssembly.h     |   5 +
 llvm/lib/Target/WebAssembly/WebAssembly.td    |   4 +
 .../WebAssembly/WebAssemblyGlobalsTagging.cpp | 141 +++++++
 .../WebAssembly/WebAssemblyInstrInfo.td       |   4 +
 .../WebAssembly/WebAssemblyInstrMemtag.td     | 233 ++++++++++++
 .../WebAssembly/WebAssemblyStackTagging.cpp   | 357 ++++++++++++++++++
 .../Target/WebAssembly/WebAssemblySubtarget.h |   2 +
 .../WebAssembly/WebAssemblyTargetMachine.cpp  |   5 +
 .../test/CodeGen/WebAssembly/memtag-addr32.ll | 282 ++++++++++++++
 .../test/CodeGen/WebAssembly/memtag-addr64.ll | 282 ++++++++++++++
 20 files changed, 1858 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/CodeGen/WebAssembly/builtins-memtag.c
 create mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyGlobalsTagging.cpp
 create mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyInstrMemtag.td
 create mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyStackTagging.cpp
 create mode 100644 llvm/test/CodeGen/WebAssembly/memtag-addr32.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/memtag-addr64.ll

diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def 
b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index a221f771820eb..dcd418f7ddee0 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -25,6 +25,33 @@
 BUILTIN(__builtin_wasm_memory_size, "zIi", "n")
 BUILTIN(__builtin_wasm_memory_grow, "zIiz", "n")
 
+// Memory Tagging
+BUILTIN(__builtin_wasm_memtag_status, "zIi", "n")
+BUILTIN(__builtin_wasm_memtag_tagbits, "zIi", "n")
+BUILTIN(__builtin_wasm_memtag_startbit, "zIi", "n")
+BUILTIN(__builtin_wasm_memtag_extract, "zIiv*", "n")
+BUILTIN(__builtin_wasm_memtag_insert, "v*Iiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_copy, "v*Iiv*v*", "n")
+BUILTIN(__builtin_wasm_memtag_sub, "YIiv*v*", "n")
+BUILTIN(__builtin_wasm_memtag_load, "v*Iiv*", "n")
+BUILTIN(__builtin_wasm_memtag_store, "vIiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_storez, "vIiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_untag, "v*Iiv*", "n")
+BUILTIN(__builtin_wasm_memtag_untagstore, "v*Iiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_untagstorez, "v*Iiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_random, "v*Iiv*", "n")
+BUILTIN(__builtin_wasm_memtag_randomstore, "v*Iiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_randomstorez, "v*Iiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_randommask, "v*Iiv*z", "n")
+BUILTIN(__builtin_wasm_memtag_randommaskstore, "v*Iiv*zz", "n")
+BUILTIN(__builtin_wasm_memtag_randommaskstorez, "v*Iiv*zz", "n")
+BUILTIN(__builtin_wasm_memtag_add, "v*Iiv*zz", "n")
+BUILTIN(__builtin_wasm_memtag_addstore, "v*Iiv*zzz", "n")
+BUILTIN(__builtin_wasm_memtag_addstorez, "v*Iiv*zzz", "n")
+BUILTIN(__builtin_wasm_memtag_hint, "v*Iiv*v*z", "n")
+BUILTIN(__builtin_wasm_memtag_hintstore, "v*Iiv*zv*z", "n")
+BUILTIN(__builtin_wasm_memtag_hintstorez, "v*Iiv*zv*z", "n")
+
 // Bulk memory builtins
 TARGET_BUILTIN(__builtin_wasm_memory_copy, "vIiIiv*vC*z", "n", "bulk-memory")
 TARGET_BUILTIN(__builtin_wasm_memory_fill, "vIiv*iz", "n", "bulk-memory")
diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp 
b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
index 9ecfc10430266..98048e2ee765a 100644
--- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
@@ -689,6 +689,207 @@ Value 
*CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
 
     return Builder.CreateCall(Callee, {TableX, TableY, SrcIdx, DstIdx, 
NElems});
   }
+  case WebAssembly::BI__builtin_wasm_memtag_status: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_status,
+                                        {ConvertType(E->getType())});
+    return Builder.CreateCall(Callee, {Index});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_tagbits: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_tagbits,
+                                        {ConvertType(E->getType())});
+    return Builder.CreateCall(Callee, {Index});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_startbit: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_startbit,
+                                        {ConvertType(E->getType())});
+    return Builder.CreateCall(Callee, {Index});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_extract: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_extract,
+                                        {ConvertType(E->getType())});
+    return Builder.CreateCall(Callee, {Index, Ptr});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_insert: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *Newtag = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_insert, {Newtag->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, Newtag});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_copy: {
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_copy);
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr0 = EmitScalarExpr(E->getArg(1));
+    Value *Ptr1 = EmitScalarExpr(E->getArg(2));
+    return Builder.CreateCall(Callee, {Index, Ptr0, Ptr1});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_sub: {
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_sub, 
ConvertType(E->getType()));
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr0 = EmitScalarExpr(E->getArg(1));
+    Value *Ptr1 = EmitScalarExpr(E->getArg(2));
+    return Builder.CreateCall(Callee, {Index, Ptr0, Ptr1});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_load: {
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_load);
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    return Builder.CreateCall(Callee, {Index, Ptr});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_store: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_store, B16->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, B16});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_storez: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_storez, B16->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, B16});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_untag: {
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_untag);
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    return Builder.CreateCall(Callee, {Index, Ptr});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_untagstore: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_untagstore, B16->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, B16});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_untagstorez: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_untagstorez, B16->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, B16});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_random: {
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_random);
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    return Builder.CreateCall(Callee, {Index, Ptr});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_randomstore: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_randomstore, B16->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, B16});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_randomstorez: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_randomstorez, B16->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, B16});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_randommask: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *Mask = EmitScalarExpr(E->getArg(2));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_randommask, Mask->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, Mask});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_randommaskstore: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Value *Mask = EmitScalarExpr(E->getArg(3));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_randommaskstore,
+                                        {B16->getType(), Mask->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, B16, Mask});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_randommaskstorez: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Value *Mask = EmitScalarExpr(E->getArg(3));
+    Function *Callee = 
CGM.getIntrinsic(Intrinsic::wasm_memtag_randommaskstorez,
+                                        {B16->getType(), Mask->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, B16, Mask});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_add: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *PtrOffset = EmitScalarExpr(E->getArg(2));
+    Value *TagOffset = EmitScalarExpr(E->getArg(3));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_add,
+                         {PtrOffset->getType(), TagOffset->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, PtrOffset, TagOffset});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_addstore: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Value *PtrOffset = EmitScalarExpr(E->getArg(3));
+    Value *TagOffset = EmitScalarExpr(E->getArg(4));
+    Function *Callee = CGM.getIntrinsic(
+        Intrinsic::wasm_memtag_addstore,
+        {B16->getType(), PtrOffset->getType(), TagOffset->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, B16, PtrOffset, TagOffset});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_addstorez: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Value *PtrOffset = EmitScalarExpr(E->getArg(3));
+    Value *TagOffset = EmitScalarExpr(E->getArg(4));
+    Function *Callee = CGM.getIntrinsic(
+        Intrinsic::wasm_memtag_addstorez,
+        {B16->getType(), PtrOffset->getType(), TagOffset->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, B16, PtrOffset, TagOffset});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_hint: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *HintPtr = EmitScalarExpr(E->getArg(2));
+    Value *HintIdx = EmitScalarExpr(E->getArg(3));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memtag_hint, HintIdx->getType());
+    return Builder.CreateCall(Callee, {Index, Ptr, HintPtr, HintIdx});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_hintstore: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Value *HintPtr = EmitScalarExpr(E->getArg(3));
+    Value *HintIdx = EmitScalarExpr(E->getArg(4));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_hintstore,
+                                        {B16->getType(), HintIdx->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, B16, HintPtr, HintIdx});
+  }
+  case WebAssembly::BI__builtin_wasm_memtag_hintstorez: {
+    Value *Index = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+    Value *B16 = EmitScalarExpr(E->getArg(2));
+    Value *HintPtr = EmitScalarExpr(E->getArg(3));
+    Value *HintIdx = EmitScalarExpr(E->getArg(4));
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memtag_hintstorez,
+                                        {B16->getType(), HintIdx->getType()});
+    return Builder.CreateCall(Callee, {Index, Ptr, B16, HintPtr, HintIdx});
+  }
   default:
     return nullptr;
   }
diff --git a/clang/lib/Driver/SanitizerArgs.cpp 
b/clang/lib/Driver/SanitizerArgs.cpp
index 778cde8285aaf..6289b399ab008 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -1754,7 +1754,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const 
llvm::opt::ArgList &Args,
         << "-fvisibility=";
   }
 
-  if (Sanitizers.has(SanitizerKind::MemtagStack) &&
+  if (Sanitizers.has(SanitizerKind::MemtagStack) && !TC.getTriple().isWasm() &&
       !hasTargetFeatureMTE(CmdArgs))
     TC.getDriver().Diag(diag::err_stack_tagging_requires_hardware_feature);
 }
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 255a9c9602b54..b853350ebe7f4 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1928,7 +1928,7 @@ ToolChain::getSupportedSanitizers(BoundArch BA,
   if (getTriple().getArch() == llvm::Triple::x86_64 ||
       getTriple().isAArch64(64) || getTriple().isRISCV())
     Res |= SanitizerKind::ShadowCallStack;
-  if (getTriple().isAArch64(64))
+  if (getTriple().isAArch64(64) || getTriple().isWasm())
     Res |= SanitizerKind::MemTag;
   if (getTriple().isBPF())
     Res |= SanitizerKind::KernelAddress;
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index b07438542aae3..b932a2efd3d8d 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1905,22 +1905,24 @@ bool tools::addSanitizerRuntimes(const ToolChain &TC, 
const ArgList &Args,
     CmdArgs.push_back("--export-dynamic-symbol=__cfi_check");
 
   if (SanArgs.hasMemTag()) {
-    CmdArgs.push_back("-z");
-    CmdArgs.push_back(
-        Args.MakeArgString("memtag-mode=" + SanArgs.getMemtagMode()));
-
-    if (SanArgs.hasMemtagHeap()) {
+    if (!TC.getTriple().isWasm()) {
       CmdArgs.push_back("-z");
-      CmdArgs.push_back("memtag-heap");
-    }
+      CmdArgs.push_back(
+          Args.MakeArgString("memtag-mode=" + SanArgs.getMemtagMode()));
 
-    if (SanArgs.hasMemtagStack()) {
-      CmdArgs.push_back("-z");
-      CmdArgs.push_back("memtag-stack");
-    }
+      if (SanArgs.hasMemtagHeap()) {
+        CmdArgs.push_back("-z");
+        CmdArgs.push_back("memtag-heap");
+      }
 
-    if (TC.getTriple().isAndroid())
-      CmdArgs.push_back("--android-memtag-note");
+      if (SanArgs.hasMemtagStack()) {
+        CmdArgs.push_back("-z");
+        CmdArgs.push_back("memtag-stack");
+      }
+
+      if (TC.getTriple().isAndroid())
+        CmdArgs.push_back("--android-memtag-note");
+    }
   }
 
   return !StaticRuntimes.empty() || !NonWholeStaticRuntimes.empty() ||
diff --git a/clang/test/CodeGen/WebAssembly/builtins-memtag.c 
b/clang/test/CodeGen/WebAssembly/builtins-memtag.c
new file mode 100644
index 0000000000000..83db7526e347c
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/builtins-memtag.c
@@ -0,0 +1,200 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm %s -o - | 
FileCheck %s --check-prefixes=CHECK,W32
+// RUN: %clang_cc1 -triple wasm64-unknown-unknown -emit-llvm %s -o - | 
FileCheck %s --check-prefixes=CHECK,W64
+
+typedef __SIZE_TYPE__ size_t;
+
+// --- 0xfc20: memtag.status ---
+// CHECK-LABEL: define {{.*}} @test_status()
+// W32: call i32 @llvm.wasm.memtag.status.i32(i32 0)
+// W64: call i64 @llvm.wasm.memtag.status.i64(i32 0)
+size_t test_status() {
+  return __builtin_wasm_memtag_status(0);
+}
+
+// --- 0xfc21: memtag.extract ---
+// CHECK-LABEL: define {{.*}} @test_extract(ptr {{.*}})
+// W32: call i32 @llvm.wasm.memtag.extract.i32(i32 0, ptr {{.*}})
+// W64: call i64 @llvm.wasm.memtag.extract.i64(i32 0, ptr {{.*}})
+size_t test_extract(void *p) {
+  return __builtin_wasm_memtag_extract(0, p);
+}
+
+// --- 0xfc22: memtag.insert ---
+// CHECK-LABEL: define {{.*}} @test_insert(ptr {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.insert.i32(i32 0, ptr {{.*}}, i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.insert.i64(i32 0, ptr {{.*}}, i64 {{.*}})
+void *test_insert(void *p, size_t v) {
+  return __builtin_wasm_memtag_insert(0, p, v);
+}
+
+// --- 0xfc23: memtag.tagbits ---
+// CHECK-LABEL: define {{.*}} @memtag_tagbits()
+// W32: call i32 @llvm.wasm.memtag.tagbits.i32(i32 0)
+// W64: call i64 @llvm.wasm.memtag.tagbits.i64(i32 0)
+size_t memtag_tagbits() {
+  return __builtin_wasm_memtag_tagbits(0);
+}
+
+// --- 0xfc24: memtag.startbit ---
+// CHECK-LABEL: define {{.*}} @memtag_startbit()
+// W32: call i32 @llvm.wasm.memtag.startbit.i32(i32 0)
+// W64: call i64 @llvm.wasm.memtag.startbit.i64(i32 0)
+size_t memtag_startbit() {
+  return __builtin_wasm_memtag_startbit(0);
+}
+
+// --- 0xfc25: memtag.copy ---
+// CHECK-LABEL: define {{.*}} @memtag_copy(ptr {{.*}}, ptr {{.*}})
+// CHECK: call ptr @llvm.wasm.memtag.copy(i32 0, ptr {{.*}}, ptr {{.*}})
+void *memtag_copy(void *d, void *s) {
+  return __builtin_wasm_memtag_copy(0, d, s);
+}
+
+// --- 0xfc26: memtag.sub ---
+// CHECK-LABEL: define {{.*}} @memtag_sub(ptr {{.*}}, ptr {{.*}})
+// W32: call i32 @llvm.wasm.memtag.sub.i32(i32 0, ptr {{.*}}, ptr {{.*}})
+// W64: call i64 @llvm.wasm.memtag.sub.i64(i32 0, ptr {{.*}}, ptr {{.*}})
+size_t memtag_sub(void *a, void *b) {
+  return __builtin_wasm_memtag_sub(0, a, b);
+}
+
+// --- 0xfc27: memtag.load ---
+// CHECK-LABEL: define {{.*}} @memtag_load(ptr {{.*}})
+// CHECK: call ptr @llvm.wasm.memtag.load(i32 0, ptr {{.*}})
+void *memtag_load(void *p) {
+  return __builtin_wasm_memtag_load(0, p);
+}
+
+// --- 0xfc28: memtag.untag ---
+// CHECK-LABEL: define {{.*}} @memtag_untag(ptr {{.*}})
+// CHECK: call ptr @llvm.wasm.memtag.untag(i32 0, ptr {{.*}})
+void *memtag_untag(void *p) {
+  return __builtin_wasm_memtag_untag(0, p);
+}
+
+// --- 0xfc29: memtag.untagstore ---
+// CHECK-LABEL: define {{.*}} @memtag_untagstore(ptr {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.untagstore.i32(i32 0, ptr {{.*}}, i32 
{{.*}})
+// W64: call ptr @llvm.wasm.memtag.untagstore.i64(i32 0, ptr {{.*}}, i64 
{{.*}})
+void *memtag_untagstore(void *p, size_t v) {
+  return __builtin_wasm_memtag_untagstore(0, p, v);
+}
+
+// --- 0xfc2a: memtag.untagstorez ---
+// CHECK-LABEL: define {{.*}} @memtag_untagstorez(ptr {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.untagstorez.i32(i32 0, ptr {{.*}}, i32 
{{.*}})
+// W64: call ptr @llvm.wasm.memtag.untagstorez.i64(i32 0, ptr {{.*}}, i64 
{{.*}})
+void *memtag_untagstorez(void *p, size_t v) {
+  return __builtin_wasm_memtag_untagstorez(0, p, v);
+}
+
+// --- 0xfc2b: memtag.store ---
+// CHECK-LABEL: define {{.*}} @memtag_store(ptr {{.*}}, {{.*}})
+// W32: call void @llvm.wasm.memtag.store.i32(i32 0, ptr {{.*}}, i32 {{.*}})
+// W64: call void @llvm.wasm.memtag.store.i64(i32 0, ptr {{.*}}, i64 {{.*}})
+void memtag_store(void *p, size_t v) {
+  __builtin_wasm_memtag_store(0, p, v);
+}
+
+// --- 0xfc2c: memtag.storez ---
+// CHECK-LABEL: define {{.*}} @memtag_storez(ptr {{.*}}, {{.*}})
+// W32: call void @llvm.wasm.memtag.storez.i32(i32 0, ptr {{.*}}, i32 {{.*}})
+// W64: call void @llvm.wasm.memtag.storez.i64(i32 0, ptr {{.*}}, i64 {{.*}})
+void memtag_storez(void *p, size_t v) {
+  __builtin_wasm_memtag_storez(0, p, v);
+}
+
+// --- 0xfc2d: memtag.random ---
+// CHECK-LABEL: define {{.*}} @memtag_random(ptr {{.*}})
+// CHECK: call ptr @llvm.wasm.memtag.random(i32 0, ptr {{.*}})
+void *memtag_random(void *p) {
+  return __builtin_wasm_memtag_random(0, p);
+}
+
+// --- 0xfc2e: memtag.randomstore ---
+// CHECK-LABEL: define {{.*}} @memtag_randomstore(ptr {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.randomstore.i32(i32 0, ptr {{.*}}, i32 
{{.*}})
+// W64: call ptr @llvm.wasm.memtag.randomstore.i64(i32 0, ptr {{.*}}, i64 
{{.*}})
+void *memtag_randomstore(void *p, size_t v) {
+  return __builtin_wasm_memtag_randomstore(0, p, v);
+}
+
+// --- 0xfc2f: memtag.randomstorez ---
+// CHECK-LABEL: define {{.*}} @memtag_randomstorez(ptr {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.randomstorez.i32(i32 0, ptr {{.*}}, i32 
{{.*}})
+// W64: call ptr @llvm.wasm.memtag.randomstorez.i64(i32 0, ptr {{.*}}, i64 
{{.*}})
+void *memtag_randomstorez(void *p, size_t v) {
+  return __builtin_wasm_memtag_randomstorez(0, p, v);
+}
+
+// --- 0xfc30: memtag.randommask ---
+// CHECK-LABEL: define {{.*}} @memtag_randommask(ptr {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.randommask.i32(i32 0, ptr {{.*}}, i32 
{{.*}})
+// W64: call ptr @llvm.wasm.memtag.randommask.i64(i32 0, ptr {{.*}}, i64 
{{.*}})
+void *memtag_randommask(void *p, size_t m) {
+  return __builtin_wasm_memtag_randommask(0, p, m);
+}
+
+// --- 0xfc31: memtag.randommaskstore ---
+// CHECK-LABEL: define {{.*}} @memtag_randommaskstore(ptr {{.*}}, {{.*}}, 
{{.*}})
+// W32: call ptr @llvm.wasm.memtag.randommaskstore.i32.i32(i32 0, ptr {{.*}}, 
i32 {{.*}}, i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.randommaskstore.i64.i64(i32 0, ptr {{.*}}, 
i64 {{.*}}, i64 {{.*}})
+void *memtag_randommaskstore(void *p, size_t v, size_t m) {
+  return __builtin_wasm_memtag_randommaskstore(0, p, v, m);
+}
+
+// --- 0xfc32: memtag.randommaskstorez ---
+// CHECK-LABEL: define {{.*}} @memtag_randommaskstorez(ptr {{.*}}, {{.*}}, 
{{.*}})
+// W32: call ptr @llvm.wasm.memtag.randommaskstorez.i32.i32(i32 0, ptr {{.*}}, 
i32 {{.*}}, i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.randommaskstorez.i64.i64(i32 0, ptr {{.*}}, 
i64 {{.*}}, i64 {{.*}})
+void *memtag_randommaskstorez(void *p, size_t v, size_t m) {
+  return __builtin_wasm_memtag_randommaskstorez(0, p, v, m);
+}
+
+// --- 0xfc33: memtag.add ---
+// CHECK-LABEL: define {{.*}} @memtag_add(ptr {{.*}}, {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.add.i32.i32(i32 0, ptr {{.*}}, i32 {{.*}}, 
i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.add.i64.i64(i32 0, ptr {{.*}}, i64 {{.*}}, 
i64 {{.*}})
+void *memtag_add(void *p, size_t o, size_t t) {
+  return __builtin_wasm_memtag_add(0, p, o, t);
+}
+
+// --- 0xfc34: memtag.addstore ---
+// CHECK-LABEL: define {{.*}} @memtag_addstore(ptr {{.*}}, {{.*}}, {{.*}}, 
{{.*}})
+// W32: call ptr @llvm.wasm.memtag.addstore.i32.i32.i32(i32 0, ptr {{.*}}, i32 
{{.*}}, i32 {{.*}}, i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.addstore.i64.i64.i64(i32 0, ptr {{.*}}, i64 
{{.*}}, i64 {{.*}}, i64 {{.*}})
+void *memtag_addstore(void *p, size_t v, size_t o, size_t t) {
+  return __builtin_wasm_memtag_addstore(0, p, v, o, t);
+}
+
+// --- 0xfc35: memtag.addstorez ---
+// CHECK-LABEL: define {{.*}} @memtag_addstorez(ptr {{.*}}, {{.*}}, {{.*}}, 
{{.*}})
+// W32: call ptr @llvm.wasm.memtag.addstorez.i32.i32.i32(i32 0, ptr {{.*}}, 
i32 {{.*}}, i32 {{.*}}, i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.addstorez.i64.i64.i64(i32 0, ptr {{.*}}, 
i64 {{.*}}, i64 {{.*}}, i64 {{.*}})
+void *memtag_addstorez(void *p, size_t v, size_t o, size_t t) {
+  return __builtin_wasm_memtag_addstorez(0, p, v, o, t);
+}
+
+// --- 0xfc36: memtag.hint ---
+// CHECK-LABEL: define {{.*}} @memtag_hint(ptr {{.*}}, ptr {{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.hint.i32(i32 0, ptr {{.*}}, ptr {{.*}}, i32 
{{.*}})
+// W64: call ptr @llvm.wasm.memtag.hint.i64(i32 0, ptr {{.*}}, ptr {{.*}}, i64 
{{.*}})
+void *memtag_hint(void *p, void *h, size_t i) {
+  return __builtin_wasm_memtag_hint(0, p, h, i);
+}
+
+// --- 0xfc37: memtag.hintstore ---
+// CHECK-LABEL: define {{.*}} @memtag_hintstore(ptr {{.*}}, {{.*}}, ptr 
{{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.hintstore.i32.i32(i32 0, ptr {{.*}}, i32 
{{.*}}, ptr {{.*}}, i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.hintstore.i64.i64(i32 0, ptr {{.*}}, i64 
{{.*}}, ptr {{.*}}, i64 {{.*}})
+void *memtag_hintstore(void *p, size_t v, void *h, size_t i) {
+  return __builtin_wasm_memtag_hintstore(0, p, v, h, i);
+}
+
+// --- 0xfc38: memtag.hintstorez ---
+// CHECK-LABEL: define {{.*}} @memtag_hintstorez(ptr {{.*}}, {{.*}}, ptr 
{{.*}}, {{.*}})
+// W32: call ptr @llvm.wasm.memtag.hintstorez.i32.i32(i32 0, ptr {{.*}}, i32 
{{.*}}, ptr {{.*}}, i32 {{.*}})
+// W64: call ptr @llvm.wasm.memtag.hintstorez.i64.i64(i32 0, ptr {{.*}}, i64 
{{.*}}, ptr {{.*}}, i64 {{.*}})
+void *memtag_hintstorez(void *p, size_t v, void *h, size_t i) {
+  return __builtin_wasm_memtag_hintstorez(0, p, v, h, i);
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td 
b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
index fbd4b35514ebf..903d58f350a49 100644
--- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
+++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
@@ -415,4 +415,83 @@ def int_wasm_tls_base :
                         [],
                         [IntrReadMem]>;
 
+//===----------------------------------------------------------------------===//
+// Memory Tagging intrinsics
+//===----------------------------------------------------------------------===//
+
+def int_wasm_memtag_status :
+  DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_i32_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_tagbits :
+  DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_i32_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_startbit :
+  DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_i32_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_extract :
+  DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_i32_ty, llvm_ptr_ty], 
[IntrNoMem]>;
+
+def int_wasm_memtag_insert :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_copy :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_ptr_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_sub :
+  DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_ptr_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_load :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty], 
[IntrReadMem]>;
+
+def int_wasm_memtag_untag :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty], 
[IntrNoMem]>;
+
+def int_wasm_memtag_untagstore :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty], [IntrWriteMem]>;
+
+def int_wasm_memtag_untagstorez :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty], [IntrWriteMem]>;
+
+def int_wasm_memtag_store :
+  DefaultAttrsIntrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_anyint_ty], 
[IntrWriteMem]>;
+
+def int_wasm_memtag_storez :
+  DefaultAttrsIntrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_anyint_ty], 
[IntrWriteMem]>;
+
+def int_wasm_memtag_random :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty], [IntrNoMem, 
IntrHasSideEffects]>;
+
+def int_wasm_memtag_randomstore :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty], [IntrHasSideEffects, IntrWriteMem]>;
+
+def int_wasm_memtag_randomstorez :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty], [IntrHasSideEffects, IntrWriteMem]>;
+
+def int_wasm_memtag_randommask :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty], [IntrNoMem, IntrHasSideEffects]>;
+
+def int_wasm_memtag_randommaskstore :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty, llvm_anyint_ty], [IntrHasSideEffects, IntrWriteMem]>;
+
+def int_wasm_memtag_randommaskstorez :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty, llvm_anyint_ty], [IntrHasSideEffects, IntrWriteMem]>;
+
+def int_wasm_memtag_add :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty, llvm_anyint_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_addstore :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty, llvm_anyint_ty, llvm_anyint_ty], [IntrWriteMem]>;
+
+def int_wasm_memtag_addstorez :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty, llvm_anyint_ty, llvm_anyint_ty], [IntrWriteMem]>;
+
+def int_wasm_memtag_hint :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, llvm_ptr_ty, 
llvm_anyint_ty], [IntrNoMem]>;
+
+def int_wasm_memtag_hintstore :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty, llvm_ptr_ty, llvm_anyint_ty], [IntrWriteMem]>;
+
+def int_wasm_memtag_hintstorez :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_i32_ty, llvm_ptr_ty, 
llvm_anyint_ty, llvm_ptr_ty, llvm_anyint_ty], [IntrWriteMem]>;
+
 } // TargetPrefix = "wasm"
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 52f8f148f7487..b7878a36ba0bc 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -839,10 +839,19 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable 
*GV,
   if (GV->isTagged()) {
     Triple T = TM.getTargetTriple();
 
-    if (T.getArch() != Triple::aarch64)
+    auto arch = T.getArch();
+
+    bool supportMemtagGlobals = false;
+
+    if (T.isWasm() || arch == Triple::aarch64) {
+      supportMemtagGlobals = true;
+    }
+
+    if (!supportMemtagGlobals)
       OutContext.reportError(SMLoc(),
                              "tagged symbols (-fsanitize=memtag-globals) are "
-                             "only supported on AArch64");
+                             "only supported on AArch64 or WebAssembly");
+
     OutStreamer->emitSymbolAttribute(EmittedSym, MCSA_Memtag);
   }
 
diff --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp
index 32f60c2089f5a..c86a034623b04 100644
--- a/llvm/lib/MC/MCWasmStreamer.cpp
+++ b/llvm/lib/MC/MCWasmStreamer.cpp
@@ -119,6 +119,12 @@ bool MCWasmStreamer::emitSymbolAttribute(MCSymbol *S, 
MCSymbolAttr Attribute) {
     Symbol->setNoStrip();
     break;
 
+  case MCSA_Memtag:
+#if 0
+    Symbol->setMemtag(true);
+#endif
+    break;
+
   default:
     // unrecognized directive
     llvm_unreachable("unexpected MCSymbolAttr");
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt 
b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index e96ac48aa00cb..ed8ca71fab4cb 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -59,6 +59,7 @@ add_llvm_target(WebAssemblyCodeGen
   WebAssemblyNullifyDebugValueLists.cpp
   WebAssemblyOptimizeLiveIntervals.cpp
   WebAssemblyOptimizeReturned.cpp
+  WebAssemblyStackTagging.cpp
   WebAssemblyPeephole.cpp
   WebAssemblyReduceToAnyAllTrue.cpp
   WebAssemblyRefTypeMem2Local.cpp
@@ -77,6 +78,7 @@ add_llvm_target(WebAssemblyCodeGen
   WebAssemblyTargetObjectFile.cpp
   WebAssemblyTargetTransformInfo.cpp
   WebAssemblyUtilities.cpp
+  WebAssemblyGlobalsTagging.cpp
 
   DEPENDS
   intrinsics_gen
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h 
b/llvm/lib/Target/WebAssembly/WebAssembly.h
index e6aefc2ba43e9..ed792e33fc64f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -325,6 +325,9 @@ class WebAssemblyMCLowerPrePass
 
 ModulePass *createWebAssemblyMCLowerPreLegacyPass();
 
+FunctionPass *createWebAssemblyStackTaggingPass();
+ModulePass *createWebAssemblyGlobalsTaggingPass();
+
 // PassRegistry initialization declarations.
 void initializeWebAssemblyOptimizeReturnedLegacyPass(PassRegistry &);
 void initializeWebAssemblyRefTypeMem2LocalLegacyPass(PassRegistry &);
@@ -358,6 +361,8 @@ void 
initializeWebAssemblyReplacePhysRegsLegacyPass(PassRegistry &);
 void initializeWebAssemblySetP2AlignOperandsLegacyPass(PassRegistry &);
 void initializeWebAssemblyCoalesceFeaturesAndStripAtomicsLegacyPass(
     PassRegistry &);
+void initializeWebAssemblyStackTaggingPass(PassRegistry &);
+void initializeWebAssemblyGlobalsTaggingPass(PassRegistry &);
 
 namespace WebAssembly {
 enum TargetIndex {
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td 
b/llvm/lib/Target/WebAssembly/WebAssembly.td
index 979c3dd716ec4..3cc56d4cb983c 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.td
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.td
@@ -96,6 +96,10 @@ def FeatureTailCall :
       SubtargetFeature<"tail-call", "HasTailCall", "true",
                        "Enable tail call instructions">;
 
+def FeatureMemtag :
+      SubtargetFeature<"memtag", "HasMemtag", "true",
+                       "Enable memory tagging">;
+
 def FeatureWideArithmetic :
       SubtargetFeature<"wide-arithmetic", "HasWideArithmetic", "true",
                        "Enable wide-arithmetic instructions">;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyGlobalsTagging.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyGlobalsTagging.cpp
new file mode 100644
index 0000000000000..3a8d0237a5cd4
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyGlobalsTagging.cpp
@@ -0,0 +1,141 @@
+//===- WebAssemblyGlobalsTagging.cpp - Global tagging in IR 
---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <set>
+
+using namespace llvm;
+
+static const Align kTagGranuleSize = Align(16);
+
+static bool shouldTagGlobal(GlobalVariable &G) {
+  if (!G.isTagged())
+    return false;
+
+  assert(G.hasSanitizerMetadata() &&
+         "Missing sanitizer metadata, but symbol is apparently tagged.");
+  GlobalValue::SanitizerMetadata Meta = G.getSanitizerMetadata();
+
+  // For now, don't instrument constant data, as it'll be in .rodata anyway. It
+  // may be worth instrumenting these in future to stop them from being used as
+  // gadgets.
+  if (G.getName().starts_with("llvm.") || G.isThreadLocal() || G.isConstant()) 
{
+    Meta.Memtag = false;
+    G.setSanitizerMetadata(Meta);
+    return false;
+  }
+
+  return true;
+}
+
+// Technically, due to ELF symbol interposition semantics, we can't change the
+// alignment or size of symbols. If we increase the alignment or size of a
+// symbol, the compiler may make optimisations based on this new alignment or
+// size. If the symbol is interposed, this optimisation could lead to
+// alignment-related or OOB read/write crashes.
+//
+// This is handled in the linker. When the linker sees multiple declarations of
+// a global variable, and some are tagged, and some are untagged, it resolves 
it
+// to be an untagged definition - but preserves the tag-granule-rounded size 
and
+// tag-granule-alignment. This should prevent these kind of crashes intra-DSO.
+// For cross-DSO, it's been a reasonable contract that if you're interposing a
+// sanitizer-instrumented global, then the interposer also needs to be
+// sanitizer-instrumented.
+//
+// FIXME: In theory, this can be fixed by splitting the size/alignment of
+// globals into two uses: an "output alignment" that's emitted to the ELF file,
+// and an "optimisation alignment" that's used for optimisation. Thus, we could
+// adjust the output alignment only, and still optimise based on the 
pessimistic
+// pre-tagging size/alignment.
+static void tagGlobalDefinition(Module &M, GlobalVariable *G) {
+  Constant *Initializer = G->getInitializer();
+  uint64_t SizeInBytes =
+      M.getDataLayout().getTypeAllocSize(Initializer->getType());
+
+  uint64_t NewSize = alignTo(SizeInBytes, kTagGranuleSize);
+  if (SizeInBytes != NewSize) {
+    // Pad the initializer out to the next multiple of 16 bytes.
+    llvm::SmallVector<uint8_t> Init(NewSize - SizeInBytes, 0);
+    Constant *Padding = ConstantDataArray::get(M.getContext(), Init);
+    Initializer = ConstantStruct::getAnon({Initializer, Padding});
+    auto *NewGV = new GlobalVariable(
+        M, Initializer->getType(), G->isConstant(), G->getLinkage(),
+        Initializer, "", G, G->getThreadLocalMode(), G->getAddressSpace());
+    NewGV->copyAttributesFrom(G);
+    NewGV->setComdat(G->getComdat());
+    NewGV->copyMetadata(G, 0);
+
+    NewGV->takeName(G);
+    G->replaceAllUsesWith(NewGV);
+    G->eraseFromParent();
+    G = NewGV;
+  }
+
+  G->setAlignment(std::max(G->getAlign().valueOrOne(), kTagGranuleSize));
+
+  // Ensure that tagged globals don't get merged by ICF - as they should have
+  // different tags at runtime.
+  G->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
+}
+
+namespace {
+class WebAssemblyGlobalsTagging : public ModulePass {
+public:
+  static char ID;
+
+  explicit WebAssemblyGlobalsTagging() : ModulePass(ID) {
+    initializeWebAssemblyGlobalsTaggingPass(*PassRegistry::getPassRegistry());
+  }
+
+  bool runOnModule(Module &M) override;
+
+  StringRef getPassName() const override {
+    return "WebAssembly Globals Tagging";
+  }
+
+private:
+  std::set<GlobalVariable *> GlobalsToTag;
+};
+} // anonymous namespace
+
+char WebAssemblyGlobalsTagging::ID = 0;
+
+bool WebAssemblyGlobalsTagging::runOnModule(Module &M) {
+  // No mutating the globals in-place, or iterator invalidation occurs.
+  std::vector<GlobalVariable *> GlobalsToTag;
+  for (GlobalVariable &G : M.globals()) {
+    if (G.isDeclaration() || !shouldTagGlobal(G))
+      continue;
+    GlobalsToTag.push_back(&G);
+  }
+  for (GlobalVariable *G : GlobalsToTag) {
+    tagGlobalDefinition(M, G);
+  }
+  return true;
+}
+
+INITIALIZE_PASS_BEGIN(WebAssemblyGlobalsTagging, "WebAssembly-globals-tagging",
+                      "WebAssembly Globals Tagging Pass", false, false)
+INITIALIZE_PASS_END(WebAssemblyGlobalsTagging, "WebAssembly-globals-tagging",
+                    "WebAssembly Globals Tagging Pass", false, false)
+
+ModulePass *llvm::createWebAssemblyGlobalsTaggingPass() {
+  return new WebAssemblyGlobalsTagging();
+}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td 
b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index 7669f301b2c48..c7d557f375930 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -57,6 +57,9 @@ def HasMultiMemory :
     Predicate<"Subtarget->hasMultiMemory()">,
     AssemblerPredicate<(all_of FeatureMultiMemory), "multimemory">;
 
+def HasMemtag :
+    Predicate<"Subtarget->hasMemtag()">,
+    AssemblerPredicate<(all_of FeatureMemtag), "memtag">;
 def HasMultivalue :
     Predicate<"Subtarget->hasMultivalue()">,
     AssemblerPredicate<(all_of FeatureMultivalue), "multivalue">;
@@ -483,3 +486,4 @@ include "WebAssemblyInstrSIMD.td"
 include "WebAssemblyInstrRef.td"
 include "WebAssemblyInstrBulkMemory.td"
 include "WebAssemblyInstrTable.td"
+include "WebAssemblyInstrMemtag.td"
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemtag.td 
b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemtag.td
new file mode 100644
index 0000000000000..ed99c9a3029ed
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemtag.td
@@ -0,0 +1,233 @@
+// WebAssemblyInstrMemtag.td - WebAssembly Memtag codegen support -*- 
memtaggen -*-
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// WebAssembly Mem Table operand code-gen constructs.
+/// Instructions that handle memtags
+//===----------------------------------------------------------------------===//
+
+multiclass MemoryTaggingOps<WebAssemblyRegClass rc, string B> {
+
+defm MEMTAG_STATUS_A#B: I<(outs rc:$statuscode), (ins i32imm:$tableidx),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$statuscode,
+                        (int_wasm_memtag_status (i32 imm:$tableidx)))],
+                    "memtag.status\t$statuscode, $tableidx",
+                    "memtag.status\t$tableidx",
+                    0xfc20>;
+
+defm MEMTAG_EXTRACT_A#B: I<(outs rc:$taggedval), (ins i32imm:$tableidx, 
rc:$ptr),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$taggedval,
+                        (int_wasm_memtag_extract (i32 imm:$tableidx), 
rc:$ptr))],
+                    "memtag.extract\t$taggedval, $tableidx, $ptr",
+                    "memtag.extract\t$tableidx",
+                    0xfc21>;
+
+defm MEMTAG_INSERT_A#B: I<(outs rc:$taggedptr), (ins i32imm:$tableidx, 
rc:$ptr, rc:$insertval),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$taggedptr,
+                        (int_wasm_memtag_insert (i32 imm:$tableidx), rc:$ptr, 
rc:$insertval))],
+                    "memtag.insert\t$taggedptr, $tableidx, $ptr, $insertval",
+                    "memtag.insert\t$tableidx",
+                    0xfc22>;
+
+defm MEMTAG_TAGBITS_A#B: I<(outs rc:$taggedbits), (ins i32imm:$tableidx),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$taggedbits,
+                        (int_wasm_memtag_tagbits (i32 imm:$tableidx)))],
+                    "memtag.tagbits\t$taggedbits, $tableidx",
+                    "memtag.tagbits\t$tableidx",
+                    0xfc23>;
+
+defm MEMTAG_STARTBIT_A#B: I<(outs rc:$startbit), (ins i32imm:$tableidx),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$startbit,
+                        (int_wasm_memtag_startbit (i32 imm:$tableidx)))],
+                    "memtag.startbit\t$startbit, $tableidx",
+                    "memtag.startbit\t$tableidx",
+                    0xfc24>;
+
+defm MEMTAG_COPY_A#B: I<(outs rc:$dstwsrctag), (ins i32imm:$tableidx, rc:$dst, 
rc:$src),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dstwsrctag,
+                        (int_wasm_memtag_copy (i32 imm:$tableidx), rc:$dst, 
rc:$src))],
+                    "memtag.copy\t$dstwsrctag, $tableidx, $dst, $src",
+                    "memtag.copy\t$tableidx",
+                    0xfc25>;
+
+defm MEMTAG_SUB_A#B: I<(outs rc:$diff), (ins i32imm:$tableidx, rc:$minuendptr, 
rc:$subtrahendptr),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$diff,
+                        (int_wasm_memtag_sub (i32 imm:$tableidx), 
rc:$minuendptr, rc:$subtrahendptr))],
+                    "memtag.sub\t$diff, $tableidx, $minuendptr, 
$subtrahendptr",
+                    "memtag.sub\t$tableidx",
+                    0xfc26>;
+
+let mayLoad = 1 in
+defm MEMTAG_LOAD_A#B: I<(outs rc:$taggedptr), (ins i32imm:$tableidx, rc:$src),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$taggedptr,
+                        (int_wasm_memtag_load (i32 imm:$tableidx), rc:$src))],
+                    "memtag.load\t$taggedptr, $tableidx, $src",
+                    "memtag.load\t$tableidx",
+                    0xfc27>;
+
+defm MEMTAG_UNTAG_A#B: I<(outs rc:$untaggedptr), (ins i32imm:$tableidx, 
rc:$ptr),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$untaggedptr,
+                        (int_wasm_memtag_untag (i32 imm:$tableidx), rc:$ptr))],
+                    "memtag.untag\t$untaggedptr, $tableidx, $ptr",
+                    "memtag.untag\t$tableidx",
+                    0xfc28>;
+
+let mayStore = 1 in
+defm MEMTAG_UNTAGSTORE_A#B: I<(outs rc:$untaggedptr), (ins i32imm:$tableidx, 
rc:$src, rc:$b16),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$untaggedptr,
+                        (int_wasm_memtag_untagstore (i32 imm:$tableidx), 
rc:$src, rc:$b16))],
+                    "memtag.untagstore\t$tableidx, $src, $b16",
+                    "memtag.untagstore\t$tableidx",
+                    0xfc29>;
+
+let mayStore = 1 in
+defm MEMTAG_UNTAGSTOREZ_A#B: I<(outs rc:$untaggedptr), (ins i32imm:$tableidx, 
rc:$src, rc:$b16),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$untaggedptr,
+                        (int_wasm_memtag_untagstorez (i32 imm:$tableidx), 
rc:$src, rc:$b16))],
+                    "memtag.untagstorez\t$tableidx, $src, $b16",
+                    "memtag.untagstorez\t$tableidx",
+                    0xfc2a>;
+
+let mayStore = 1 in
+defm MEMTAG_STORE_A#B: I<(outs), (ins i32imm:$tableidx, rc:$src, rc:$b16),
+                    (outs), (ins i32imm:$tableidx),
+                    [(int_wasm_memtag_store (i32 imm:$tableidx), rc:$src, 
rc:$b16)],
+                    "memtag.store\t$tableidx, $src, $b16",
+                    "memtag.store\t$tableidx",
+                    0xfc2b>;
+
+let mayStore = 1 in
+defm MEMTAG_STOREZ_A#B: I<(outs), (ins i32imm:$tableidx, rc:$src, rc:$b16),
+                    (outs), (ins i32imm:$tableidx),
+                    [(int_wasm_memtag_storez (i32 imm:$tableidx), rc:$src, 
rc:$b16)],
+                    "memtag.storez\t$tableidx, $src, $b16",
+                    "memtag.storez\t$tableidx",
+                    0xfc2c>;
+
+let hasSideEffects = 1 in
+defm MEMTAG_RANDOM_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_random (i32 imm:$tableidx), 
rc:$src))],
+                    "memtag.random\t$dst, $tableidx, $src",
+                    "memtag.random\t$tableidx",
+                    0xfc2d>;
+
+let hasSideEffects = 1, mayStore = 1 in
+defm MEMTAG_RANDOMSTORE_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$b16),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_randomstore (i32 imm:$tableidx), 
rc:$src, rc:$b16))],
+                    "memtag.randomstore\t$dst, $tableidx, $src, $b16",
+                    "memtag.randomstore\t$tableidx",
+                    0xfc2e>;
+
+let hasSideEffects = 1, mayStore = 1 in
+defm MEMTAG_RANDOMSTOREZ_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, 
rc:$src, rc:$b16),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_randomstorez (i32 imm:$tableidx), 
rc:$src, rc:$b16))],
+                    "memtag.randomstorez\t$dst, $src, $tableidx, $b16",
+                    "memtag.randomstorez\t$tableidx",
+                    0xfc2f>;
+
+let hasSideEffects = 1 in
+defm MEMTAG_RANDOMMASK_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$mask),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_randommask (i32 imm:$tableidx), 
rc:$src, rc:$mask))],
+                    "memtag.randommask\t$dst, $tableidx, $src, $mask",
+                    "memtag.randommask\t$tableidx",
+                    0xfc30>;
+
+
+let hasSideEffects = 1, mayStore = 1 in
+defm MEMTAG_RANDOMMASKSTORE_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, 
rc:$src, rc:$b16, rc:$mask),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_randommaskstore (i32 imm:$tableidx), 
rc:$src, rc:$b16, rc:$mask))],
+                    "memtag.randommaskstore\t$dst, $tableidx, $src, $b16, 
$mask",
+                    "memtag.randommaskstore\t$tableidx",
+                    0xfc31>;
+
+let hasSideEffects = 1, mayStore = 1 in
+defm MEMTAG_RANDOMMASKSTOREZ_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, 
rc:$src, rc:$b16, rc:$mask),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_randommaskstorez (i32 imm:$tableidx), 
rc:$src, rc:$b16, rc:$mask))],
+                    "memtag.randommaskstorez\t$dst, $src, $tableidx, $b16, 
$mask",
+                    "memtag.randommaskstorez\t$tableidx",
+                    0xfc32>;
+
+defm MEMTAG_ADD_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$addroffset, rc:$tagoffset),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_add (i32 imm:$tableidx), rc:$src, 
rc:$addroffset, rc:$tagoffset))],
+                    "memtag.add\t$dst, $tableidx, $src, $addroffset, 
$tagoffset",
+                    "memtag.add\t$tableidx",
+                    0xfc33>;
+
+let mayStore = 1 in
+defm MEMTAG_ADDSTORE_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$b16, rc:$addroffset, rc:$tagoffset),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_addstore (i32 imm:$tableidx), 
rc:$src, rc:$b16, rc:$addroffset, rc:$tagoffset))],
+                    "memtag.addstore\t$dst, $tableidx, $src, $b16, 
$addroffset, $tagoffset",
+                    "memtag.addstore\t$tableidx",
+                    0xfc34>;
+
+let mayStore = 1 in
+defm MEMTAG_ADDSTOREZ_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$b16, rc:$addroffset, rc:$tagoffset),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_addstorez (i32 imm:$tableidx), 
rc:$src, rc:$b16, rc:$addroffset, rc:$tagoffset))],
+                    "memtag.addstorez\t$dst, $tableidx, $src, $b16, 
$addroffset, $tagoffset",
+                    "memtag.addstorez\t$tableidx",
+                    0xfc35>;
+
+defm MEMTAG_HINT_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$hintaddr, rc:$index),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_hint (i32 imm:$tableidx), rc:$src, 
rc:$hintaddr, rc:$index))],
+                    "memtag.hint\t$dst, $tableidx, $src, $hintaddr, $index",
+                    "memtag.hint\t$tableidx",
+                    0xfc36>;
+
+let mayStore = 1 in
+defm MEMTAG_HINTSTORE_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$b16, rc:$hintaddr, rc:$index),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_hintstore (i32 imm:$tableidx), 
rc:$src, rc:$b16, rc:$hintaddr, rc:$index))],
+                    "memtag.hintstore\t$dst, $tableidx, $src, $b16, $hintaddr, 
$index",
+                    "memtag.hintstore\t$tableidx",
+                    0xfc37>;
+
+let mayStore = 1 in
+defm MEMTAG_HINTSTOREZ_A#B: I<(outs rc:$dst), (ins i32imm:$tableidx, rc:$src, 
rc:$b16, rc:$hintaddr, rc:$index),
+                    (outs), (ins i32imm:$tableidx),
+                    [(set rc:$dst,
+                        (int_wasm_memtag_hintstorez (i32 imm:$tableidx), 
rc:$src, rc:$b16, rc:$hintaddr, rc:$index))],
+                    "memtag.hintstorez\t$dst, $tableidx, $src, $b16, 
$hintaddr, $index",
+                    "memtag.hintstorez\t$tableidx",
+                    0xfc38>;
+
+}
+
+defm : MemoryTaggingOps<I32, "32">;
+defm : MemoryTaggingOps<I64, "64">;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyStackTagging.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyStackTagging.cpp
new file mode 100644
index 0000000000000..f6514008a05a3
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyStackTagging.cpp
@@ -0,0 +1,357 @@
+//===- WebAssemblyTargetMachine.cpp - Define TargetMachine for WebAssembly 
-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "WebAssembly.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/StackSafetyAnalysis.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/ValueHandle.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/Transforms/Utils/MemoryTaggingSupport.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-stack-tagging"
+
+namespace {
+
+struct WebAssemblyStackTagging : public FunctionPass {
+  static char ID;
+  StackSafetyGlobalInfo const *SSI = nullptr;
+  DataLayout const *DL = nullptr;
+  AAResults *AA = nullptr;
+  WebAssemblyStackTagging() : FunctionPass(ID) {}
+
+  void untagAlloca(AllocaInst *AI, Instruction *InsertBefore, uint64_t Size,
+                   Function *StoreTagDecl, Type *ArgOp0Type);
+
+  Instruction *insertBaseTaggedPointer(
+      const MapVector<AllocaInst *, memtag::AllocaInfo> &Allocas,
+      const DominatorTree *DT);
+
+  bool runOnFunction(Function &) override;
+
+private:
+  Function *F = nullptr;
+#if 1
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesCFG();
+    AU.addRequired<StackSafetyGlobalInfoWrapperPass>();
+#if 0
+    if (MergeInit)
+      AU.addRequired<AAResultsWrapperPass>();
+#endif
+    AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
+  }
+#endif
+};
+
+inline bool canCombineStore(const memtag::AllocaInfo &Info, AllocaInst *AI,
+                            DominatorTree *DT) {
+  // Must have exactly one lifetime interval.
+  if (Info.LifetimeStart.size() != 1 || Info.LifetimeEnd.size() != 1)
+    return false;
+
+  IntrinsicInst *Start = Info.LifetimeStart.front();
+
+  // Check dominance and instruction order for every use.
+  for (User *U : AI->users()) {
+    auto *I = dyn_cast<Instruction>(U);
+    if (!I)
+      continue;
+
+    // Lifetime intrinsics are not rewritten and do not matter.
+    if (isa<LifetimeIntrinsic>(I))
+      continue;
+
+    // If Start does not dominate the use, combined tagging is unsafe.
+    if (!DT->dominates(Start, I))
+      return false;
+
+    // If in the same block, Start must come before the use.
+    if (Start->getParent() == I->getParent() && !Start->comesBefore(I))
+      return false;
+  }
+
+  return true;
+}
+
+static const inline Align kTagGranuleSize = Align(16);
+
+} // namespace
+
+void WebAssemblyStackTagging::untagAlloca(AllocaInst *AI,
+                                          Instruction *InsertBefore,
+                                          uint64_t Size, Function 
*StoreTagDecl,
+                                          Type *ArgOp0Type) {
+
+  IRBuilder<> IRB(InsertBefore);
+  IRB.CreateCall(StoreTagDecl,
+                 {IRB.getInt32(0), AI, ConstantInt::get(ArgOp0Type, Size)});
+}
+
+Instruction *WebAssemblyStackTagging::insertBaseTaggedPointer(
+    const MapVector<AllocaInst *, memtag::AllocaInfo> &AllocasToInstrument,
+    const DominatorTree *DT) {
+  BasicBlock *PrologueBB = nullptr;
+  // Try sinking IRG as deep as possible to avoid hurting shrink wrap.
+  for (auto &I : AllocasToInstrument) {
+    const memtag::AllocaInfo &Info = I.second;
+    AllocaInst *AI = Info.AI;
+    if (!PrologueBB) {
+      PrologueBB = AI->getParent();
+      continue;
+    }
+    PrologueBB = DT->findNearestCommonDominator(PrologueBB, AI->getParent());
+  }
+  assert(PrologueBB);
+
+  IRBuilder<> IRB(&PrologueBB->front());
+  Function *RdTag = Intrinsic::getOrInsertDeclaration(
+      F->getParent(), Intrinsic::wasm_memtag_random);
+  Instruction *Base =
+      IRB.CreateCall(RdTag, {IRB.getInt32(0),
+                             
::llvm::ConstantPointerNull::get(IRB.getPtrTy())});
+  Base->setName("basetag");
+  return Base;
+}
+
+bool WebAssemblyStackTagging::runOnFunction(Function &Fn) {
+  if (!Fn.hasFnAttribute(Attribute::SanitizeMemTag))
+    return false;
+
+  Triple triplet(Fn.getParent()->getTargetTriple());
+  bool iswasm32 = triplet.getArch() == ::llvm::Triple::wasm32;
+
+  F = &Fn;
+  DL = &Fn.getParent()->getDataLayout();
+
+  OptimizationRemarkEmitter &ORE =
+      getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
+
+  SSI = &getAnalysis<StackSafetyGlobalInfoWrapperPass>().getResult();
+  memtag::StackInfoBuilder SIB(SSI, "webassembly-stack-tagging");
+  for (Instruction &I : instructions(F))
+    SIB.visit(ORE, I);
+  memtag::StackInfo &SInfo = SIB.get();
+
+  std::unique_ptr<DominatorTree> DeleteDT;
+  DominatorTree *DT = nullptr;
+  if (auto *P = getAnalysisIfAvailable<DominatorTreeWrapperPass>())
+    DT = &P->getDomTree();
+
+  if (DT == nullptr) {
+    DeleteDT = std::make_unique<DominatorTree>(*F);
+    DT = DeleteDT.get();
+  }
+
+  std::unique_ptr<PostDominatorTree> DeletePDT;
+  PostDominatorTree *PDT = nullptr;
+  if (auto *P = getAnalysisIfAvailable<PostDominatorTreeWrapperPass>())
+    PDT = &P->getPostDomTree();
+
+  if (PDT == nullptr) {
+    DeletePDT = std::make_unique<PostDominatorTree>(*F);
+    PDT = DeletePDT.get();
+  }
+
+  std::unique_ptr<LoopInfo> DeleteLI;
+  LoopInfo *LI = nullptr;
+  if (auto *LIWP = getAnalysisIfAvailable<LoopInfoWrapperPass>()) {
+    LI = &LIWP->getLoopInfo();
+  } else {
+    DeleteLI = std::make_unique<LoopInfo>(*DT);
+    LI = DeleteLI.get();
+  }
+  auto &AllocasToInstrument = SInfo.AllocasToInstrument;
+  if (AllocasToInstrument.empty()) {
+    return true;
+  }
+  Instruction *Base = nullptr;
+  bool usehint = false;
+  if (1 < AllocasToInstrument.size()) {
+    Base = insertBaseTaggedPointer(AllocasToInstrument, DT);
+    usehint = true;
+  }
+  uint64_t NextTag = 0;
+  LLVMContext &Ctx = Fn.getContext();
+  Type *Int32Type = llvm::Type::getInt32Ty(Ctx);
+  Type *Int64Type = llvm::Type::getInt64Ty(Ctx);
+  Type *IntPtrType = iswasm32 ? Int32Type : Int64Type;
+
+  Function *UntagStoreDecl = Intrinsic::getOrInsertDeclaration(
+      F->getParent(), Intrinsic::wasm_memtag_untagstore, {IntPtrType});
+
+  for (auto &I : AllocasToInstrument) {
+    memtag::AllocaInfo &Info = I.second;
+    memtag::alignAndPadAlloca(Info, kTagGranuleSize);
+    uint64_t Tag = NextTag;
+    if (iswasm32) {
+      Tag = static_cast<uint32_t>(Tag);
+    }
+    ++NextTag;
+    AllocaInst *AI = Info.AI;
+    IRBuilder<> IRB(Info.AI->getNextNode());
+
+    // Calls to functions that may return twice (e.g. setjmp) confuse the
+    // postdominator analysis, and will leave us to keep memory tagged after
+    // function return. Work around this by always untagging at every return
+    // statement if return_twice functions are called.
+    bool SupportedLifetime =
+        memtag::isSupportedLifetime(Info, DT, LI) && !SInfo.CallsReturnTwice;
+    if (SupportedLifetime) {
+      bool combineStore{canCombineStore(Info, AI, DT)};
+      Function *StoreTagDecl = nullptr;
+      Intrinsic::ID SelectedIntrinsicID;
+
+      if (combineStore) {
+        SelectedIntrinsicID = usehint ? Intrinsic::wasm_memtag_hintstore
+                                      : Intrinsic::wasm_memtag_randomstore;
+      } else {
+        SelectedIntrinsicID = usehint ? Intrinsic::wasm_memtag_hint
+                                      : Intrinsic::wasm_memtag_random;
+        StoreTagDecl = Intrinsic::getOrInsertDeclaration(
+            F->getParent(), Intrinsic::wasm_memtag_store, {IntPtrType});
+      }
+
+      SmallVector<Type *, 2> SelectedStoreSignatureTypes;
+      if (combineStore) {
+        SelectedStoreSignatureTypes.push_back(IntPtrType);
+      }
+      if (usehint) {
+        SelectedStoreSignatureTypes.push_back(IntPtrType);
+      }
+      uint64_t Size = *Info.AI->getAllocationSize(*DL);
+      Size = alignTo(Size, kTagGranuleSize);
+
+      auto *RandomOrHintMayStoreTagDecl = Intrinsic::getOrInsertDeclaration(
+          F->getParent(), SelectedIntrinsicID, SelectedStoreSignatureTypes);
+
+      SmallVector<Value *, 5> TagCallArguments{ConstantInt::get(Int32Type, 0),
+                                               Info.AI};
+      if (combineStore) {
+        TagCallArguments.push_back(ConstantInt::get(IntPtrType, Size));
+      }
+      if (usehint) {
+        TagCallArguments.push_back(Base);
+        TagCallArguments.push_back(ConstantInt::get(IntPtrType, Tag));
+      }
+      if (combineStore) {
+        IntrinsicInst *Start = Info.LifetimeStart.front();
+        IRBuilder<> IRBStart(Start->getNextNode());
+        CallInst *TagPCall =
+            IRBStart.CreateCall(RandomOrHintMayStoreTagDecl, TagCallArguments);
+        if (Info.AI->hasName())
+          TagPCall->setName(Info.AI->getName() + ".tag");
+
+        Info.AI->replaceUsesWithIf(TagPCall, [&](const Use &U) {
+          return U.getUser() != TagPCall &&
+                 !isa<LifetimeIntrinsic>(U.getUser());
+        });
+        TagPCall->setOperand(1, Info.AI);
+        IntrinsicInst *End = Info.LifetimeEnd.front();
+        IRBuilder<> IRBEnd(End);
+        IRBEnd.CreateCall(UntagStoreDecl,
+                          {ConstantInt::get(Int32Type, 0), Info.AI,
+                           ConstantInt::get(IntPtrType, Size)});
+        Start->eraseFromParent();
+        End->eraseFromParent();
+      } else {
+        auto *TagPCall =
+            IRB.CreateCall(RandomOrHintMayStoreTagDecl, TagCallArguments);
+
+        if (Info.AI->hasName())
+          TagPCall->setName(Info.AI->getName() + ".tag");
+
+        Info.AI->replaceUsesWithIf(TagPCall, [&](const Use &U) {
+          return !isa<LifetimeIntrinsic>(U.getUser());
+        });
+
+        TagPCall->setOperand(1, Info.AI);
+
+        for (IntrinsicInst *Start : Info.LifetimeStart) {
+          IRBuilder<> IRB2(Start->getNextNode());
+          IRB2.CreateCall(StoreTagDecl,
+                          {ConstantInt::get(Int32Type, 0), TagPCall,
+                           ConstantInt::get(IntPtrType, Size)});
+        }
+        auto TagEnd = [&](Instruction *Node) {
+          untagAlloca(AI, Node, Size, UntagStoreDecl, IntPtrType);
+        };
+        memtag::forAllReachableExits(*DT, *PDT, *LI, Info, SInfo.RetVec,
+                                     TagEnd);
+      }
+    } else {
+      uint64_t Size = *Info.AI->getAllocationSize(*DL);
+      Intrinsic::ID SelectedStoreIntrinsicID =
+          usehint ? Intrinsic::wasm_memtag_hintstore
+                  : Intrinsic::wasm_memtag_randomstore;
+      SmallVector<Type *, 2> SelectedStoreSignatureTypes{IntPtrType};
+      if (usehint) {
+        SelectedStoreSignatureTypes.push_back(IntPtrType);
+      }
+      auto *RandomOrHintStoreTagDecl = Intrinsic::getOrInsertDeclaration(
+          F->getParent(), SelectedStoreIntrinsicID,
+          SelectedStoreSignatureTypes);
+
+      SmallVector<Value *, 5> StoreTagCallArguments{
+          ConstantInt::get(Int32Type, 0), Info.AI,
+          ConstantInt::get(IntPtrType, Size)};
+      if (usehint) {
+        StoreTagCallArguments.push_back(Base);
+        StoreTagCallArguments.push_back(ConstantInt::get(IntPtrType, Tag));
+      }
+
+      auto *TagPCall =
+          IRB.CreateCall(RandomOrHintStoreTagDecl, StoreTagCallArguments);
+      if (Info.AI->hasName())
+        TagPCall->setName(Info.AI->getName() + ".tag");
+      Info.AI->replaceAllUsesWith(TagPCall);
+      TagPCall->setOperand(1, Info.AI);
+      for (auto *RI : SInfo.RetVec) {
+        untagAlloca(AI, RI, Size, UntagStoreDecl, IntPtrType);
+      }
+      // We may have inserted tag/untag outside of any lifetime interval.
+      // Remove all lifetime intrinsics for this alloca.
+      for (auto *II : Info.LifetimeStart)
+        II->eraseFromParent();
+      for (auto *II : Info.LifetimeEnd)
+        II->eraseFromParent();
+    }
+
+    memtag::annotateDebugRecords(Info, static_cast<unsigned long>(Tag));
+  }
+
+  return true;
+}
+
+char WebAssemblyStackTagging::ID = 0;
+INITIALIZE_PASS_BEGIN(WebAssemblyStackTagging, DEBUG_TYPE,
+                      "WebAssembly Stack Tagging", false, false)
+#if 0
+INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
+#endif
+INITIALIZE_PASS_DEPENDENCY(StackSafetyGlobalInfoWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass)
+INITIALIZE_PASS_END(WebAssemblyStackTagging, DEBUG_TYPE,
+                    "WebAssembly Stack Tagging", false, false)
+
+FunctionPass *llvm::createWebAssemblyStackTaggingPass() {
+  return new WebAssemblyStackTagging();
+}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h 
b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
index 149c49dd34473..8e5db9522e3f1 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
@@ -55,6 +55,7 @@ class WebAssemblySubtarget final : public 
WebAssemblyGenSubtargetInfo {
   bool HasMutableGlobals = false;
   bool HasNontrappingFPToInt = false;
   bool HasReferenceTypes = false;
+  bool HasMemtag = false;
   bool HasRelaxedAtomics = false;
   bool HasSignExt = false;
   bool HasTailCall = false;
@@ -123,6 +124,7 @@ class WebAssemblySubtarget final : public 
WebAssemblyGenSubtargetInfo {
   bool hasMutableGlobals() const { return HasMutableGlobals; }
   bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; }
   bool hasReferenceTypes() const { return HasReferenceTypes; }
+  bool hasMemtag() const { return HasMemtag; }
   bool hasRelaxedAtomics() const { return HasRelaxedAtomics; }
   bool hasRelaxedSIMD() const { return SIMDLevel >= RelaxedSIMD; }
   bool hasSignExt() const { return HasSignExt; }
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 066e8329e7f39..ce7b06b7e71ca 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -115,6 +115,8 @@ LLVMInitializeWebAssemblyTarget() {
   initializeWebAssemblyMCLowerPreLegacyPass(PR);
   initializeWebAssemblyFixBrTableDefaultsLegacyPass(PR);
   initializeWebAssemblyDAGToDAGISelLegacyPass(PR);
+  initializeWebAssemblyStackTaggingPass(PR);
+  initializeWebAssemblyGlobalsTaggingPass(PR);
 }
 
 
//===----------------------------------------------------------------------===//
@@ -319,6 +321,9 @@ void WebAssemblyPassConfig::addIRPasses() {
   if (getOptLevel() != CodeGenOptLevel::None)
     addPass(createWebAssemblyOptimizeReturnedLegacyPass());
 
+  addPass(createWebAssemblyGlobalsTaggingPass());
+  addPass(createWebAssemblyStackTaggingPass());
+
   // If exception handling is not enabled and setjmp/longjmp handling is
   // enabled, we lower invokes into calls and delete unreachable landingpad
   // blocks. Lowering invokes when there is no EH support is done in
diff --git a/llvm/test/CodeGen/WebAssembly/memtag-addr32.ll 
b/llvm/test/CodeGen/WebAssembly/memtag-addr32.ll
new file mode 100644
index 0000000000000..222e110f41f60
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/memtag-addr32.ll
@@ -0,0 +1,282 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+target triple = "wasm32-unknown-unknown"
+
+; --- 0xfc20: memtag.status ---
+; CHECK-LABEL: test_status:
+; CHECK: memtag.status 0
+define i32 @test_status() {
+  %1 = call i32 @llvm.wasm.memtag.status.i32(i32 0)
+  ret i32 %1
+}
+
+; --- 0xfc21: memtag.extract ---
+; CHECK-LABEL: test_extract:
+; CHECK: local.get 0
+; CHECK: memtag.extract 0
+define i32 @test_extract(ptr %p) {
+  %1 = call i32 @llvm.wasm.memtag.extract.i32(i32 0, ptr %p)
+  ret i32 %1
+}
+
+; --- 0xfc22: memtag.insert ---
+; CHECK-LABEL: test_insert:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.insert 0
+define ptr @test_insert(ptr %p, i32 %v) {
+  %1 = call ptr @llvm.wasm.memtag.insert.i32(i32 0, ptr %p, i32 %v)
+  ret ptr %1
+}
+
+; --- 0xfc23: memtag.tagbits ---
+; CHECK-LABEL: test_tagbits:
+; CHECK: memtag.tagbits 0
+define i32 @test_tagbits() {
+  %1 = call i32 @llvm.wasm.memtag.tagbits.i32(i32 0)
+  ret i32 %1
+}
+
+; --- 0xfc24: memtag.startbit ---
+; CHECK-LABEL: test_startbit:
+; CHECK: memtag.startbit 0
+define i32 @test_startbit() {
+  %1 = call i32 @llvm.wasm.memtag.startbit.i32(i32 0)
+  ret i32 %1
+}
+
+; --- 0xfc25: memtag.copy ---
+; CHECK-LABEL: test_copy:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.copy 0
+define ptr @test_copy(ptr %d, ptr %s) {
+  %1 = call ptr @llvm.wasm.memtag.copy(i32 0, ptr %d, ptr %s)
+  ret ptr %1
+}
+
+; --- 0xfc26: memtag.sub ---
+; CHECK-LABEL: test_sub:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.sub 0
+define i32 @test_sub(ptr %a, ptr %b) {
+  %1 = call i32 @llvm.wasm.memtag.sub.i32(i32 0, ptr %a, ptr %b)
+  ret i32 %1
+}
+
+; --- 0xfc27: memtag.load ---
+; CHECK-LABEL: test_load:
+; CHECK: local.get 0
+; CHECK: memtag.load 0
+define ptr @test_load(ptr %p) {
+  %1 = call ptr @llvm.wasm.memtag.load(i32 0, ptr %p)
+  ret ptr %1
+}
+
+; --- 0xfc28: memtag.untag ---
+; CHECK-LABEL: test_untag:
+; CHECK: local.get 0
+; CHECK: memtag.untag 0
+define ptr @test_untag(ptr %p) {
+  %1 = call ptr @llvm.wasm.memtag.untag(i32 0, ptr %p)
+  ret ptr %1
+}
+
+; --- 0xfc29: memtag.untagstore ---
+; CHECK-LABEL: test_untagstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.untagstore 0
+define ptr @test_untagstore(ptr %p, i32 %v) {
+  %1 = call ptr @llvm.wasm.memtag.untagstore.i32(i32 0, ptr %p, i32 %v)
+  ret ptr %1
+}
+
+; --- 0xfc2a: memtag.untagstorez ---
+; CHECK-LABEL: test_untagstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.untagstorez 0
+define ptr @test_untagstorez(ptr %p, i32 %v) {
+  %1 = call ptr @llvm.wasm.memtag.untagstorez.i32(i32 0, ptr %p, i32 %v)
+  ret ptr %1
+}
+
+; --- 0xfc2b: memtag.store ---
+; CHECK-LABEL: test_store:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.store 0
+define void @test_store(ptr %p, i32 %v) {
+  call void @llvm.wasm.memtag.store.i32(i32 0, ptr %p, i32 %v)
+  ret void
+}
+
+; --- 0xfc2c: memtag.storez ---
+; CHECK-LABEL: test_storez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.storez 0
+define void @test_storez(ptr %p, i32 %v) {
+  call void @llvm.wasm.memtag.storez.i32(i32 0, ptr %p, i32 %v)
+  ret void
+}
+
+; --- 0xfc2d: memtag.random ---
+; CHECK-LABEL: test_random:
+; CHECK: local.get 0
+; CHECK: memtag.random 0
+define ptr @test_random(ptr %p) {
+  %1 = call ptr @llvm.wasm.memtag.random(i32 0, ptr %p)
+  ret ptr %1
+}
+
+; --- 0xfc2e: memtag.randomstore ---
+; CHECK-LABEL: test_randomstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.randomstore 0
+define ptr @test_randomstore(ptr %p, i32 %v) {
+  %1 = call ptr @llvm.wasm.memtag.randomstore.i32(i32 0, ptr %p, i32 %v)
+  ret ptr %1
+}
+
+; --- 0xfc2f: memtag.randomstorez ---
+; CHECK-LABEL: test_randomstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.randomstorez 0
+define ptr @test_randomstorez(ptr %p, i32 %v) {
+  %1 = call ptr @llvm.wasm.memtag.randomstorez.i32(i32 0, ptr %p, i32 %v)
+  ret ptr %1
+}
+
+; --- 0xfc30: memtag.randommask ---
+; CHECK-LABEL: test_randommask:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.randommask 0
+define ptr @test_randommask(ptr %p, i32 %m) {
+  %1 = call ptr @llvm.wasm.memtag.randommask.i32(i32 0, ptr %p, i32 %m)
+  ret ptr %1
+}
+
+; --- 0xfc31: memtag.randommaskstore ---
+; CHECK-LABEL: test_randommaskstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.randommaskstore 0
+define ptr @test_randommaskstore(ptr %p, i32 %v, i32 %m) {
+  %1 = call ptr @llvm.wasm.memtag.randommaskstore.i32(i32 0, ptr %p, i32 %v, 
i32 %m)
+  ret ptr %1
+}
+
+; --- 0xfc32: memtag.randommaskstorez ---
+; CHECK-LABEL: test_randommaskstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.randommaskstorez 0
+define ptr @test_randommaskstorez(ptr %p, i32 %v, i32 %m) {
+  %1 = call ptr @llvm.wasm.memtag.randommaskstorez.i32(i32 0, ptr %p, i32 %v, 
i32 %m)
+  ret ptr %1
+}
+
+; --- 0xfc33: memtag.add ---
+; CHECK-LABEL: test_add:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.add 0
+define ptr @test_add(ptr %p, i32 %o, i32 %t) {
+  %1 = call ptr @llvm.wasm.memtag.add.i32(i32 0, ptr %p, i32 %o, i32 %t)
+  ret ptr %1
+}
+
+; --- 0xfc34: memtag.addstore ---
+; CHECK-LABEL: test_addstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.addstore 0
+define ptr @test_addstore(ptr %p, i32 %v, i32 %o, i32 %t) {
+  %1 = call ptr @llvm.wasm.memtag.addstore.i32(i32 0, ptr %p, i32 %v, i32 %o, 
i32 %t)
+  ret ptr %1
+}
+
+; --- 0xfc35: memtag.addstorez ---
+; CHECK-LABEL: test_addstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.addstorez 0
+define ptr @test_addstorez(ptr %p, i32 %v, i32 %o, i32 %t) {
+  %1 = call ptr @llvm.wasm.memtag.addstorez.i32(i32 0, ptr %p, i32 %v, i32 %o, 
i32 %t)
+  ret ptr %1
+}
+
+; --- 0xfc36: memtag.hint ---
+; CHECK-LABEL: test_hint:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.hint 0
+define ptr @test_hint(ptr %p, ptr %h, i32 %i) {
+  %1 = call ptr @llvm.wasm.memtag.hint.i32(i32 0, ptr %p, ptr %h, i32 %i)
+  ret ptr %1
+}
+
+; --- 0xfc37: memtag.hintstore ---
+; CHECK-LABEL: test_hintstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.hintstore 0
+define ptr @test_hintstore(ptr %p, i32 %v, ptr %h, i32 %i) {
+  %1 = call ptr @llvm.wasm.memtag.hintstore.i32(i32 0, ptr %p, i32 %v, ptr %h, 
i32 %i)
+  ret ptr %1
+}
+
+; --- 0xfc38: memtag.hintstorez ---
+; CHECK-LABEL: test_hintstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.hintstorez 0
+define ptr @test_hintstorez(ptr %p, i32 %v, ptr %h, i32 %i) {
+  %1 = call ptr @llvm.wasm.memtag.hintstorez.i32(i32 0, ptr %p, i32 %v, ptr 
%h, i32 %i)
+  ret ptr %1
+}
+
+; --- Intrinsic Declarations ---
+declare i32 @llvm.wasm.memtag.status.i32(i32)
+declare i32 @llvm.wasm.memtag.extract.i32(i32, ptr)
+declare ptr @llvm.wasm.memtag.insert.i32(i32, ptr, i32)
+declare i32 @llvm.wasm.memtag.tagbits.i32(i32)
+declare i32 @llvm.wasm.memtag.startbit.i32(i32)
+declare ptr @llvm.wasm.memtag.copy(i32, ptr, ptr)
+declare i32 @llvm.wasm.memtag.sub.i32(i32, ptr, ptr)
+declare ptr @llvm.wasm.memtag.load(i32, ptr)
+declare ptr @llvm.wasm.memtag.untag(i32, ptr)
+declare ptr @llvm.wasm.memtag.untagstore.i32(i32, ptr, i32)
+declare ptr @llvm.wasm.memtag.untagstorez.i32(i32, ptr, i32)
+declare void @llvm.wasm.memtag.store.i32(i32, ptr, i32)
+declare void @llvm.wasm.memtag.storez.i32(i32, ptr, i32)
+declare ptr @llvm.wasm.memtag.random(i32, ptr)
+declare ptr @llvm.wasm.memtag.randomstore.i32(i32, ptr, i32)
+declare ptr @llvm.wasm.memtag.randomstorez.i32(i32, ptr, i32)
+declare ptr @llvm.wasm.memtag.randommask.i32(i32, ptr, i32)
+declare ptr @llvm.wasm.memtag.randommaskstore.i32(i32, ptr, i32, i32)
+declare ptr @llvm.wasm.memtag.randommaskstorez.i32(i32, ptr, i32, i32)
+declare ptr @llvm.wasm.memtag.add.i32(i32, ptr, i32, i32)
+declare ptr @llvm.wasm.memtag.addstore.i32(i32, ptr, i32, i32, i32)
+declare ptr @llvm.wasm.memtag.addstorez.i32(i32, ptr, i32, i32, i32)
+declare ptr @llvm.wasm.memtag.hint.i32(i32, ptr, ptr, i32)
+declare ptr @llvm.wasm.memtag.hintstore.i32(i32, ptr, i32, ptr, i32)
+declare ptr @llvm.wasm.memtag.hintstorez.i32(i32, ptr, i32, ptr, i32)
diff --git a/llvm/test/CodeGen/WebAssembly/memtag-addr64.ll 
b/llvm/test/CodeGen/WebAssembly/memtag-addr64.ll
new file mode 100644
index 0000000000000..b23100bf0416b
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/memtag-addr64.ll
@@ -0,0 +1,282 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+target triple = "wasm64-unknown-unknown"
+
+; --- 0xfc20: memtag.status ---
+; CHECK-LABEL: test_status:
+; CHECK: memtag.status 0
+define i64 @test_status() {
+  %1 = call i64 @llvm.wasm.memtag.status.i64(i32 0)
+  ret i64 %1
+}
+
+; --- 0xfc21: memtag.extract ---
+; CHECK-LABEL: test_extract:
+; CHECK: local.get 0
+; CHECK: memtag.extract 0
+define i64 @test_extract(ptr %p) {
+  %1 = call i64 @llvm.wasm.memtag.extract.i64(i32 0, ptr %p)
+  ret i64 %1
+}
+
+; --- 0xfc22: memtag.insert ---
+; CHECK-LABEL: test_insert:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.insert 0
+define ptr @test_insert(ptr %p, i64 %v) {
+  %1 = call ptr @llvm.wasm.memtag.insert.i64(i32 0, ptr %p, i64 %v)
+  ret ptr %1
+}
+
+; --- 0xfc23: memtag.tagbits ---
+; CHECK-LABEL: test_tagbits:
+; CHECK: memtag.tagbits 0
+define i64 @test_tagbits() {
+  %1 = call i64 @llvm.wasm.memtag.tagbits.i64(i32 0)
+  ret i64 %1
+}
+
+; --- 0xfc24: memtag.startbit ---
+; CHECK-LABEL: test_startbit:
+; CHECK: memtag.startbit 0
+define i64 @test_startbit() {
+  %1 = call i64 @llvm.wasm.memtag.startbit.i64(i32 0)
+  ret i64 %1
+}
+
+; --- 0xfc25: memtag.copy ---
+; CHECK-LABEL: test_copy:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.copy 0
+define ptr @test_copy(ptr %d, ptr %s) {
+  %1 = call ptr @llvm.wasm.memtag.copy(i32 0, ptr %d, ptr %s)
+  ret ptr %1
+}
+
+; --- 0xfc26: memtag.sub ---
+; CHECK-LABEL: test_sub:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.sub 0
+define i64 @test_sub(ptr %a, ptr %b) {
+  %1 = call i64 @llvm.wasm.memtag.sub.i64(i32 0, ptr %a, ptr %b)
+  ret i64 %1
+}
+
+; --- 0xfc27: memtag.load ---
+; CHECK-LABEL: test_load:
+; CHECK: local.get 0
+; CHECK: memtag.load 0
+define ptr @test_load(ptr %p) {
+  %1 = call ptr @llvm.wasm.memtag.load(i32 0, ptr %p)
+  ret ptr %1
+}
+
+; --- 0xfc28: memtag.untag ---
+; CHECK-LABEL: test_untag:
+; CHECK: local.get 0
+; CHECK: memtag.untag 0
+define ptr @test_untag(ptr %p) {
+  %1 = call ptr @llvm.wasm.memtag.untag(i32 0, ptr %p)
+  ret ptr %1
+}
+
+; --- 0xfc29: memtag.untagstore ---
+; CHECK-LABEL: test_untagstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.untagstore 0
+define ptr @test_untagstore(ptr %p, i64 %v) {
+  %1 = call ptr @llvm.wasm.memtag.untagstore.i64(i32 0, ptr %p, i64 %v)
+  ret ptr %1
+}
+
+; --- 0xfc2a: memtag.untagstorez ---
+; CHECK-LABEL: test_untagstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.untagstorez 0
+define ptr @test_untagstorez(ptr %p, i64 %v) {
+  %1 = call ptr @llvm.wasm.memtag.untagstorez.i64(i32 0, ptr %p, i64 %v)
+  ret ptr %1
+}
+
+; --- 0xfc2b: memtag.store ---
+; CHECK-LABEL: test_store:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.store 0
+define void @test_store(ptr %p, i64 %v) {
+  call void @llvm.wasm.memtag.store.i64(i32 0, ptr %p, i64 %v)
+  ret void
+}
+
+; --- 0xfc2c: memtag.storez ---
+; CHECK-LABEL: test_storez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.storez 0
+define void @test_storez(ptr %p, i64 %v) {
+  call void @llvm.wasm.memtag.storez.i64(i32 0, ptr %p, i64 %v)
+  ret void
+}
+
+; --- 0xfc2d: memtag.random ---
+; CHECK-LABEL: test_random:
+; CHECK: local.get 0
+; CHECK: memtag.random 0
+define ptr @test_random(ptr %p) {
+  %1 = call ptr @llvm.wasm.memtag.random(i32 0, ptr %p)
+  ret ptr %1
+}
+
+; --- 0xfc2e: memtag.randomstore ---
+; CHECK-LABEL: test_randomstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.randomstore 0
+define ptr @test_randomstore(ptr %p, i64 %v) {
+  %1 = call ptr @llvm.wasm.memtag.randomstore.i64(i32 0, ptr %p, i64 %v)
+  ret ptr %1
+}
+
+; --- 0xfc2f: memtag.randomstorez ---
+; CHECK-LABEL: test_randomstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.randomstorez 0
+define ptr @test_randomstorez(ptr %p, i64 %v) {
+  %1 = call ptr @llvm.wasm.memtag.randomstorez.i64(i32 0, ptr %p, i64 %v)
+  ret ptr %1
+}
+
+; --- 0xfc30: memtag.randommask ---
+; CHECK-LABEL: test_randommask:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: memtag.randommask 0
+define ptr @test_randommask(ptr %p, i64 %m) {
+  %1 = call ptr @llvm.wasm.memtag.randommask.i64(i32 0, ptr %p, i64 %m)
+  ret ptr %1
+}
+
+; --- 0xfc31: memtag.randommaskstore ---
+; CHECK-LABEL: test_randommaskstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.randommaskstore 0
+define ptr @test_randommaskstore(ptr %p, i64 %v, i64 %m) {
+  %1 = call ptr @llvm.wasm.memtag.randommaskstore.i64(i32 0, ptr %p, i64 %v, 
i64 %m)
+  ret ptr %1
+}
+
+; --- 0xfc32: memtag.randommaskstorez ---
+; CHECK-LABEL: test_randommaskstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.randommaskstorez 0
+define ptr @test_randommaskstorez(ptr %p, i64 %v, i64 %m) {
+  %1 = call ptr @llvm.wasm.memtag.randommaskstorez.i64(i32 0, ptr %p, i64 %v, 
i64 %m)
+  ret ptr %1
+}
+
+; --- 0xfc33: memtag.add ---
+; CHECK-LABEL: test_add:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.add 0
+define ptr @test_add(ptr %p, i64 %o, i64 %t) {
+  %1 = call ptr @llvm.wasm.memtag.add.i64(i32 0, ptr %p, i64 %o, i64 %t)
+  ret ptr %1
+}
+
+; --- 0xfc34: memtag.addstore ---
+; CHECK-LABEL: test_addstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.addstore 0
+define ptr @test_addstore(ptr %p, i64 %v, i64 %o, i64 %t) {
+  %1 = call ptr @llvm.wasm.memtag.addstore.i64(i32 0, ptr %p, i64 %v, i64 %o, 
i64 %t)
+  ret ptr %1
+}
+
+; --- 0xfc35: memtag.addstorez ---
+; CHECK-LABEL: test_addstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.addstorez 0
+define ptr @test_addstorez(ptr %p, i64 %v, i64 %o, i64 %t) {
+  %1 = call ptr @llvm.wasm.memtag.addstorez.i64(i32 0, ptr %p, i64 %v, i64 %o, 
i64 %t)
+  ret ptr %1
+}
+
+; --- 0xfc36: memtag.hint ---
+; CHECK-LABEL: test_hint:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: memtag.hint 0
+define ptr @test_hint(ptr %p, ptr %h, i64 %i) {
+  %1 = call ptr @llvm.wasm.memtag.hint.i64(i32 0, ptr %p, ptr %h, i64 %i)
+  ret ptr %1
+}
+
+; --- 0xfc37: memtag.hintstore ---
+; CHECK-LABEL: test_hintstore:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.hintstore 0
+define ptr @test_hintstore(ptr %p, i64 %v, ptr %h, i64 %i) {
+  %1 = call ptr @llvm.wasm.memtag.hintstore.i64(i32 0, ptr %p, i64 %v, ptr %h, 
i64 %i)
+  ret ptr %1
+}
+
+; --- 0xfc38: memtag.hintstorez ---
+; CHECK-LABEL: test_hintstorez:
+; CHECK: local.get 0
+; CHECK: local.get 1
+; CHECK: local.get 2
+; CHECK: local.get 3
+; CHECK: memtag.hintstorez 0
+define ptr @test_hintstorez(ptr %p, i64 %v, ptr %h, i64 %i) {
+  %1 = call ptr @llvm.wasm.memtag.hintstorez.i64(i32 0, ptr %p, i64 %v, ptr 
%h, i64 %i)
+  ret ptr %1
+}
+
+; --- Intrinsic Declarations ---
+declare i64 @llvm.wasm.memtag.status.i64(i32)
+declare i64 @llvm.wasm.memtag.extract.i64(i32, ptr)
+declare ptr @llvm.wasm.memtag.insert.i64(i32, ptr, i64)
+declare i64 @llvm.wasm.memtag.tagbits.i64(i32)
+declare i64 @llvm.wasm.memtag.startbit.i64(i32)
+declare ptr @llvm.wasm.memtag.copy(i32, ptr, ptr)
+declare i64 @llvm.wasm.memtag.sub.i64(i32, ptr, ptr)
+declare ptr @llvm.wasm.memtag.load(i32, ptr)
+declare ptr @llvm.wasm.memtag.untag(i32, ptr)
+declare ptr @llvm.wasm.memtag.untagstore.i64(i32, ptr, i64)
+declare ptr @llvm.wasm.memtag.untagstorez.i64(i32, ptr, i64)
+declare void @llvm.wasm.memtag.store.i64(i32, ptr, i64)
+declare void @llvm.wasm.memtag.storez.i64(i32, ptr, i64)
+declare ptr @llvm.wasm.memtag.random(i32, ptr)
+declare ptr @llvm.wasm.memtag.randomstore.i64(i32, ptr, i64)
+declare ptr @llvm.wasm.memtag.randomstorez.i64(i32, ptr, i64)
+declare ptr @llvm.wasm.memtag.randommask.i64(i32, ptr, i64)
+declare ptr @llvm.wasm.memtag.randommaskstore.i64(i32, ptr, i64, i64)
+declare ptr @llvm.wasm.memtag.randommaskstorez.i64(i32, ptr, i64, i64)
+declare ptr @llvm.wasm.memtag.add.i64(i32, ptr, i64, i64)
+declare ptr @llvm.wasm.memtag.addstore.i64(i32, ptr, i64, i64, i64)
+declare ptr @llvm.wasm.memtag.addstorez.i64(i32, ptr, i64, i64, i64)
+declare ptr @llvm.wasm.memtag.hint.i64(i32, ptr, ptr, i64)
+declare ptr @llvm.wasm.memtag.hintstore.i64(i32, ptr, i64, ptr, i64)
+declare ptr @llvm.wasm.memtag.hintstorez.i64(i32, ptr, i64, ptr, i64)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to