[clang] [llvm] [WebAssembly][Sanitizer] WebAssembly Memory Tagging (PR #162972)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 19 13:05:39 PDT 2026
https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/162972
>From b2309c93f36530fa497093744e4c984dd05cbee4 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
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)
More information about the cfe-commits
mailing list