https://github.com/sbc100 updated 
https://github.com/llvm/llvm-project/pull/220111

>From 34e5d02dc5c0fa38e6920b9d5fd561c3be85899b Mon Sep 17 00:00:00 2001
From: Sam Clegg <[email protected]>
Date: Mon, 31 Aug 2026 15:20:37 -0700
Subject: [PATCH] [WebAssembly] Add intrinsics and builtins for memory.copy and
 memory.fill

Add `@llvm.wasm.memory.copy` and `@llvm.wasm.memory.fill` intrinsics,
along with corresponding `__builtin_wasm_memory_copy` and
`__builtin_wasm_memory_fill` Clang builtins.

These map directly to the WebAssembly `memory.copy` and `memory.fill`
instructions, allowing low-level runtime and libc code (such as
Emscripten) to emit bulk memory operations directly in C/C++ without
needing out-of-line assembly or going through `llvm.memcpy` /
`llvm.memset` (which lower with zero-length branching checks to
conform to C semantics).

Fixes: #220086
---
 clang/docs/LanguageExtensions.md              | 38 +++++++++++++++++
 clang/docs/ReleaseNotes.md                    |  3 ++
 .../clang/Basic/BuiltinsWebAssembly.def       |  4 ++
 .../CodeGen/TargetBuiltins/WebAssembly.cpp    | 19 +++++++++
 clang/test/CodeGen/builtins-wasm.c            | 12 ++++++
 llvm/docs/ReleaseNotes.md                     |  2 +
 llvm/include/llvm/IR/IntrinsicsWebAssembly.td | 20 +++++++++
 .../WebAssembly/WebAssemblyInstrBulkMemory.td |  5 ++-
 .../WebAssembly/bulk-memory-intrinsics.ll     | 42 +++++++++++++++++++
 .../WebAssembly/bulk-memory-intrinsics64.ll   | 42 +++++++++++++++++++
 10 files changed, 185 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics64.ll

diff --git a/clang/docs/LanguageExtensions.md b/clang/docs/LanguageExtensions.md
index 6289a2ddc6c56..e596c9bebb37c 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -3040,6 +3040,44 @@ void copy(int dst, int src, int nelem) {
 }
 ```
 
+### `__builtin_wasm_memory_copy`
+
+This builtin function copies bytes from a source memory to a possibly
+overlapping destination region using the WebAssembly `memory.copy` instruction.
+It takes five arguments:
+1. Destination memory index (must be a constant integer)
+2. Source memory index (must be a constant integer)
+3. Destination pointer (`void *`)
+4. Source pointer (`const void *`)
+5. Number of bytes to copy (`size_t`)
+
+It returns nothing. Note that unlike C `memcpy` or `memmove`, `memory.copy`
+traps if either pointer is out of bounds even when the number of bytes is zero.
+
+```c++
+void copy(void *dst, const void *src, size_t n) {
+  __builtin_wasm_memory_copy(0, 0, dst, src, n);
+}
+```
+
+### `__builtin_wasm_memory_fill`
+
+This builtin function sets bytes in memory using the WebAssembly `memory.fill`
+instruction. It takes four arguments:
+1. Memory index (must be a constant integer)
+2. Destination pointer (`void *`)
+3. Byte value to set (passed as `int`, lowest 8 bits used)
+4. Number of bytes to set (`size_t`)
+
+It returns nothing. Note that unlike C `memset`, `memory.fill` traps if the
+pointer is out of bounds even when the number of bytes is zero.
+
+```c++
+void fill(void *dst, int val, size_t n) {
+  __builtin_wasm_memory_fill(0, dst, val, n);
+}
+```
+
 ## Builtin Functions
 
 Clang supports a number of builtin library functions with the same syntax as
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a616bd41f3560..f6c9cf32c9cc0 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -687,6 +687,9 @@ features cannot lower the translation-unit ABI level;
 
 #### WebAssembly Support
 
+- Added `__builtin_wasm_memory_copy` and `__builtin_wasm_memory_fill` builtins
+  for the WebAssembly `memory.copy` and `memory.fill` bulk memory instructions.
+
 #### AVR Support
 
 #### SystemZ Support
diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def 
b/clang/include/clang/Basic/BuiltinsWebAssembly.def
index 6de620cb22f28..a221f771820eb 100644
--- a/clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -25,6 +25,10 @@
 BUILTIN(__builtin_wasm_memory_size, "zIi", "n")
 BUILTIN(__builtin_wasm_memory_grow, "zIiz", "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")
+
 // Thread-local storage
 TARGET_BUILTIN(__builtin_wasm_tls_size, "z", "nc", "bulk-memory")
 TARGET_BUILTIN(__builtin_wasm_tls_align, "z", "nc", "bulk-memory")
diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp 
b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
index e7bdb91d49ce8..9ecfc10430266 100644
--- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
@@ -39,6 +39,25 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned 
BuiltinID,
         CGM.getIntrinsic(Intrinsic::wasm_memory_grow, ResultType);
     return Builder.CreateCall(Callee, Args);
   }
+  case WebAssembly::BI__builtin_wasm_memory_copy: {
+    Value *DstMem = EmitScalarExpr(E->getArg(0));
+    Value *SrcMem = EmitScalarExpr(E->getArg(1));
+    Value *Dst = EmitScalarExpr(E->getArg(2));
+    Value *Src = EmitScalarExpr(E->getArg(3));
+    Value *Size = EmitScalarExpr(E->getArg(4));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memory_copy, Size->getType());
+    return Builder.CreateCall(Callee, {DstMem, SrcMem, Dst, Src, Size});
+  }
+  case WebAssembly::BI__builtin_wasm_memory_fill: {
+    Value *Mem = EmitScalarExpr(E->getArg(0));
+    Value *Dst = EmitScalarExpr(E->getArg(1));
+    Value *Val = EmitScalarExpr(E->getArg(2));
+    Value *Size = EmitScalarExpr(E->getArg(3));
+    Function *Callee =
+        CGM.getIntrinsic(Intrinsic::wasm_memory_fill, Size->getType());
+    return Builder.CreateCall(Callee, {Mem, Dst, Val, Size});
+  }
   case WebAssembly::BI__builtin_wasm_tls_size: {
     llvm::Type *ResultType = ConvertType(E->getType());
     Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_tls_size, ResultType);
diff --git a/clang/test/CodeGen/builtins-wasm.c 
b/clang/test/CodeGen/builtins-wasm.c
index 40788b0afeb45..776e73c580d3e 100644
--- a/clang/test/CodeGen/builtins-wasm.c
+++ b/clang/test/CodeGen/builtins-wasm.c
@@ -27,6 +27,18 @@ __SIZE_TYPE__ memory_grow(__SIZE_TYPE__ delta) {
   // WEBASSEMBLY64: call i64 @llvm.wasm.memory.grow.i64(i32 0, i64 %{{.*}})
 }
 
+void memory_copy(void *dest, const void *src, __SIZE_TYPE__ count) {
+  __builtin_wasm_memory_copy(0, 0, dest, src, count);
+  // WEBASSEMBLY32: call void @llvm.wasm.memory.copy.i32(i32 0, i32 0, ptr 
%{{.*}}, ptr %{{.*}}, i32 %{{.*}})
+  // WEBASSEMBLY64: call void @llvm.wasm.memory.copy.i64(i32 0, i32 0, ptr 
%{{.*}}, ptr %{{.*}}, i64 %{{.*}})
+}
+
+void memory_fill(void *dest, int value, __SIZE_TYPE__ count) {
+  __builtin_wasm_memory_fill(0, dest, value, count);
+  // WEBASSEMBLY32: call void @llvm.wasm.memory.fill.i32(i32 0, ptr %{{.*}}, 
i32 %{{.*}}, i32 %{{.*}})
+  // WEBASSEMBLY64: call void @llvm.wasm.memory.fill.i64(i32 0, ptr %{{.*}}, 
i32 %{{.*}}, i64 %{{.*}})
+}
+
 __SIZE_TYPE__ tls_size(void) {
   return __builtin_wasm_tls_size();
   // WEBASSEMBLY32: call i32 @llvm.wasm.tls.size.i32()
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index ef90a1f1f1c41..5c288a0ee6fa7 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -222,6 +222,8 @@ Makes programs 10x faster by doing Special New Thing.
 
 * Added support for emitting common symbols (.comm) using the 
WASM_SYMBOL_BINDING_COMMON
   flag (see https://github.com/WebAssembly/tool-conventions/pull/267)
+* Added `@llvm.wasm.memory.copy` and `@llvm.wasm.memory.fill` intrinsics for
+  the WebAssembly `memory.copy` and `memory.fill` instructions.
 
 ### Changes to the Windows Target
 
diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td 
b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
index a0e83cee9f055..fbd4b35514ebf 100644
--- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
+++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
@@ -24,6 +24,26 @@ def int_wasm_memory_size :
 def int_wasm_memory_grow :
   DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_i32_ty, LLVMMatchType<0>], []>;
 
+//===----------------------------------------------------------------------===//
+// Bulk memory intrinsics
+//===----------------------------------------------------------------------===//
+
+def int_wasm_memory_copy :
+  DefaultAttrsIntrinsic<[],
+                        [llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty, llvm_ptr_ty,
+                         llvm_anyint_ty],
+                        [IntrArgMemOnly, NoCapture<ArgIndex<2>>,
+                         NoCapture<ArgIndex<3>>, WriteOnly<ArgIndex<2>>,
+                         ReadOnly<ArgIndex<3>>, ImmArg<ArgIndex<0>>,
+                         ImmArg<ArgIndex<1>>]>;
+
+def int_wasm_memory_fill :
+  DefaultAttrsIntrinsic<[],
+                        [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty,
+                         llvm_anyint_ty],
+                        [IntrWriteMem, IntrArgMemOnly, NoCapture<ArgIndex<1>>,
+                         WriteOnly<ArgIndex<1>>, ImmArg<ArgIndex<0>>]>;
+
 
//===----------------------------------------------------------------------===//
 // ref.null intrinsics
 
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td 
b/llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td
index ba79867b942f9..f842c2e27b6c0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td
@@ -59,7 +59,8 @@ defm COPY_A#B :
   BULK_I<(outs), (ins i32imm_op:$dst_idx, i32imm_op:$src_idx,
                       rc:$dst, rc:$src, rc:$len),
          (outs), (ins i32imm_op:$dst_idx, i32imm_op:$src_idx),
-         [],
+         [(int_wasm_memory_copy (i32 timm:$dst_idx), (i32 timm:$src_idx),
+                                rc:$dst, rc:$src, rc:$len)],
          "memory.copy\t$dst_idx, $src_idx, $dst, $src, $len",
          "memory.copy\t$dst_idx, $src_idx", 0x0a>;
 
@@ -67,7 +68,7 @@ let mayStore = 1 in
 defm FILL_A#B :
   BULK_I<(outs), (ins i32imm_op:$idx, rc:$dst, I32:$value, rc:$size),
          (outs), (ins i32imm_op:$idx),
-         [],
+         [(int_wasm_memory_fill (i32 timm:$idx), rc:$dst, I32:$value, 
rc:$size)],
          "memory.fill\t$idx, $dst, $value, $size",
          "memory.fill\t$idx", 0x0b>;
 }
diff --git a/llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics.ll 
b/llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics.ll
new file mode 100644
index 0000000000000..403aefc06d859
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics.ll
@@ -0,0 +1,42 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs 
-disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals 
-wasm-keep-registers -mcpu=mvp -mattr=+bulk-memory,+multimemory | FileCheck %s
+
+target triple = "wasm32-unknown-unknown"
+
+declare void @llvm.wasm.memory.copy.i32(i32, i32, ptr, ptr, i32)
+declare void @llvm.wasm.memory.fill.i32(i32, ptr, i32, i32)
+
+; CHECK-LABEL: memory_copy:
+; CHECK-NEXT: .functype memory_copy (i32, i32, i32) -> ()
+; CHECK-NEXT: memory.copy 0, 0, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_copy(ptr %dest, ptr %src, i32 %len) {
+  call void @llvm.wasm.memory.copy.i32(i32 0, i32 0, ptr %dest, ptr %src, i32 
%len)
+  ret void
+}
+
+; CHECK-LABEL: memory_fill:
+; CHECK-NEXT: .functype memory_fill (i32, i32, i32) -> ()
+; CHECK-NEXT: memory.fill 0, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_fill(ptr %dest, i32 %value, i32 %len) {
+  call void @llvm.wasm.memory.fill.i32(i32 0, ptr %dest, i32 %value, i32 %len)
+  ret void
+}
+
+; CHECK-LABEL: memory_copy_multi:
+; CHECK-NEXT: .functype memory_copy_multi (i32, i32, i32) -> ()
+; CHECK-NEXT: memory.copy 1, 2, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_copy_multi(ptr %dest, ptr %src, i32 %len) {
+  call void @llvm.wasm.memory.copy.i32(i32 1, i32 2, ptr %dest, ptr %src, i32 
%len)
+  ret void
+}
+
+; CHECK-LABEL: memory_fill_multi:
+; CHECK-NEXT: .functype memory_fill_multi (i32, i32, i32) -> ()
+; CHECK-NEXT: memory.fill 3, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_fill_multi(ptr %dest, i32 %value, i32 %len) {
+  call void @llvm.wasm.memory.fill.i32(i32 3, ptr %dest, i32 %value, i32 %len)
+  ret void
+}
diff --git a/llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics64.ll 
b/llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics64.ll
new file mode 100644
index 0000000000000..a71c21097ddf5
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics64.ll
@@ -0,0 +1,42 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs 
-disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals 
-wasm-keep-registers -mcpu=mvp -mattr=+bulk-memory,+multimemory | FileCheck %s
+
+target triple = "wasm64-unknown-unknown"
+
+declare void @llvm.wasm.memory.copy.i64(i32, i32, ptr, ptr, i64)
+declare void @llvm.wasm.memory.fill.i64(i32, ptr, i32, i64)
+
+; CHECK-LABEL: memory_copy:
+; CHECK-NEXT: .functype memory_copy (i64, i64, i64) -> ()
+; CHECK-NEXT: memory.copy 0, 0, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_copy(ptr %dest, ptr %src, i64 %len) {
+  call void @llvm.wasm.memory.copy.i64(i32 0, i32 0, ptr %dest, ptr %src, i64 
%len)
+  ret void
+}
+
+; CHECK-LABEL: memory_fill:
+; CHECK-NEXT: .functype memory_fill (i64, i32, i64) -> ()
+; CHECK-NEXT: memory.fill 0, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_fill(ptr %dest, i32 %value, i64 %len) {
+  call void @llvm.wasm.memory.fill.i64(i32 0, ptr %dest, i32 %value, i64 %len)
+  ret void
+}
+
+; CHECK-LABEL: memory_copy_multi:
+; CHECK-NEXT: .functype memory_copy_multi (i64, i64, i64) -> ()
+; CHECK-NEXT: memory.copy 1, 2, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_copy_multi(ptr %dest, ptr %src, i64 %len) {
+  call void @llvm.wasm.memory.copy.i64(i32 1, i32 2, ptr %dest, ptr %src, i64 
%len)
+  ret void
+}
+
+; CHECK-LABEL: memory_fill_multi:
+; CHECK-NEXT: .functype memory_fill_multi (i64, i32, i64) -> ()
+; CHECK-NEXT: memory.fill 3, $0, $1, $2
+; CHECK-NEXT: return
+define void @memory_fill_multi(ptr %dest, i32 %value, i64 %len) {
+  call void @llvm.wasm.memory.fill.i64(i32 3, ptr %dest, i32 %value, i64 %len)
+  ret void
+}

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

Reply via email to