https://github.com/banach-space updated 
https://github.com/llvm/llvm-project/pull/176781

From 9052a77786541403905ef684c3c9fe2d4f7862c4 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <[email protected]>
Date: Mon, 19 Jan 2026 15:42:06 +0000
Subject: [PATCH] [CIR] Add cir.libc.memcpy Op

The operation is a 1:1 mapping to libc's memcpy.

NOTE: This patch merely upstreams code from
  * https://github.com/llvm/clangir.

This Op was originally implemented by Vinicius Couto Espindola. Further
modification were made by other ClangIR contributors.

co-authored-by: Vinicius Couto Espindola <[email protected]>
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 47 +++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         | 10 ++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  9 ++++
 clang/test/CIR/IR/invalid-memcpy.cir          | 37 +++++++++++++++
 clang/test/CIR/IR/libc-memcpy.cir             | 10 ++++
 clang/test/CIR/Lowering/libc.cir              | 12 +++++
 6 files changed, 125 insertions(+)
 create mode 100644 clang/test/CIR/IR/invalid-memcpy.cir
 create mode 100644 clang/test/CIR/IR/libc-memcpy.cir
 create mode 100644 clang/test/CIR/Lowering/libc.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e31024f7dfa84..9cb7138467840 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3439,6 +3439,53 @@ def CIR_CopyOp : CIR_Op<"copy",[
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// MemCpyOp && MemMoveOp
+//===----------------------------------------------------------------------===//
+
+class CIR_MemOp<string mnemonic> : CIR_Op<mnemonic, [
+  AllTypesMatch<["dst", "src"]>
+]> {
+  dag commonArgs = (ins
+    Arg<CIR_VoidPtrType, "", [MemWrite]>:$dst,
+    Arg<CIR_VoidPtrType, "", [MemRead]>:$src
+  );
+}
+
+def CIR_MemCpyOp : CIR_MemOp<"libc.memcpy"> {
+  let summary = "Equivalent to libc's `memcpy`";
+  let description = [{
+    Given two CIR pointers, `src` and `dst`, `cir.libc.memcpy` will copy `len`
+    bytes from the memory pointed by `src` to the memory pointed by `dst`.
+
+    While `cir.copy` is meant to be used for implicit copies in the code where
+    the length of the copy is known, `cir.memcpy` copies only from and to void
+    pointers, requiring the copy length to be passed as an argument.
+
+    Examples:
+
+    ```mlir
+      // Copying 2 bytes from one array to a record:
+      %2 = cir.const #cir.int<2> : !u32i
+      cir.libc.memcpy %2 bytes from %arr to %record : !cir.ptr<!arr> -> 
!cir.ptr<!record>
+    ```
+  }];
+
+  let arguments = !con(commonArgs, (ins CIR_AnyFundamentalUIntType:$len));
+
+  let assemblyFormat = [{
+    $len `bytes` `from` $src `to` $dst attr-dict
+    `:` type($len) `` `,` qualified(type($src)) `->` qualified(type($dst))
+  }];
+
+  let extraClassDeclaration = [{
+    /// Returns the byte length type.
+    cir::IntType getLenTy() { return getLen().getType(); }
+  }];
+}
+
+// TODO: MemMoveOp
+
 
//===----------------------------------------------------------------------===//
 // ReturnAddrOp and FrameAddrOp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index ff492edf0b04e..dc1ce9a901381 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -189,6 +189,16 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
     return getType<cir::RecordType>(nameAttr, kind);
   }
 
+  //
+  // Operation creation helpers
+  // --------------------------
+  //
+  cir::MemCpyOp createMemCpy(mlir::Location loc, mlir::Value dst,
+                             mlir::Value src, mlir::Value len) {
+    return cir::MemCpyOp::create(*this, loc, dst, src, len);
+  }
+  // ---------------------------
+
   cir::DataMemberAttr getDataMemberAttr(cir::DataMemberType ty,
                                         unsigned memberIndex) {
     return cir::DataMemberAttr::get(ty, memberIndex);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 7625bcccd520f..a774b0dcc6ba8 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -187,6 +187,15 @@ mlir::LogicalResult 
CIRToLLVMCopyOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRToLLVMMemCpyOpLowering::matchAndRewrite(
+    cir::MemCpyOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
+      op, adaptor.getDst(), adaptor.getSrc(), adaptor.getLen(),
+      /*isVolatile=*/false);
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRToLLVMSqrtOpLowering::matchAndRewrite(
     cir::SqrtOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/IR/invalid-memcpy.cir 
b/clang/test/CIR/IR/invalid-memcpy.cir
new file mode 100644
index 0000000000000..805d03adcb95f
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-memcpy.cir
@@ -0,0 +1,37 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s8i = !cir.int<s, 8>
+module {
+  // Should not memcpy with invalid length type.
+  cir.func @invalid_memcpy_len(%arg0 : !cir.ptr<!cir.void>, %arg1 : !s8i) {
+    // expected-error@+1 {{'cir.libc.memcpy' op operand #2 must be fundamental 
unsigned integer type, but got '!cir.int<s, 8>'}}
+    cir.libc.memcpy %arg1 bytes from %arg0 to %arg0 : !s8i, 
!cir.ptr<!cir.void> -> !cir.ptr<!cir.void>
+    cir.return
+  }
+}
+
+// -----
+
+!s8i = !cir.int<s, 8>
+!u32i = !cir.int<u, 32>
+module {
+  // Should not memcpy non-void pointers.
+  cir.func @invalid_memcpy_pointer_0(%arg0 : !cir.ptr<!s8i>, %arg1 : !u32i) {
+    // expected-error@+1 {{'cir.libc.memcpy' op operand #0 must be pointer to 
void type, but got '!cir.ptr<!cir.int<s, 8>>'}}
+    cir.libc.memcpy %arg1 bytes from %arg0 to %arg0 : !u32i, !cir.ptr<!s8i> -> 
!cir.ptr<!s8i>
+    cir.return
+  }
+}
+
+// -----
+
+!s8i = !cir.int<s, 8>
+!u32i = !cir.int<u, 32>
+module {
+  // Should not memcpy non-void pointers.
+  cir.func @invalid_memcpy_pointer_1(%arg0 : !cir.ptr<!cir.void>, %arg1 : 
!cir.ptr<!s8i>, %arg2 : !u32i) {
+    // expected-error@+1 {{'cir.libc.memcpy' op operand #1 must be pointer to 
void type, but got '!cir.ptr<!cir.int<s, 8>>'}}
+    cir.libc.memcpy %arg2 bytes from %arg1 to %arg0 : !u32i, !cir.ptr<!s8i> -> 
!cir.ptr<!cir.void>
+    cir.return
+  }
+}
diff --git a/clang/test/CIR/IR/libc-memcpy.cir 
b/clang/test/CIR/IR/libc-memcpy.cir
new file mode 100644
index 0000000000000..6769092f3beb1
--- /dev/null
+++ b/clang/test/CIR/IR/libc-memcpy.cir
@@ -0,0 +1,10 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!u32i = !cir.int<u, 32>
+module {
+  cir.func @shouldParseLibcMemcpyOp(%arg0 : !cir.ptr<!cir.void>, %arg1 : 
!u32i) {
+    // CHECK: cir.libc.memcpy 
+    cir.libc.memcpy %arg1 bytes from %arg0 to %arg0 : !u32i, 
!cir.ptr<!cir.void> -> !cir.ptr<!cir.void>
+    cir.return
+  }
+}
diff --git a/clang/test/CIR/Lowering/libc.cir b/clang/test/CIR/Lowering/libc.cir
new file mode 100644
index 0000000000000..74e384d08a74b
--- /dev/null
+++ b/clang/test/CIR/Lowering/libc.cir
@@ -0,0 +1,12 @@
+// RUN: cir-opt %s -cir-to-llvm -o %t.mlir
+// RUN: FileCheck --input-file=%t.mlir %s
+
+!void = !cir.void
+!u64i = !cir.int<u, 64>
+module {
+  cir.func @shouldLowerLibcMemcpyBuiltin(%arg0: !cir.ptr<!void>, %arg1: 
!cir.ptr<!void>, %arg2: !u64i) {
+    cir.libc.memcpy %arg2 bytes from %arg0 to %arg1 : !u64i, !cir.ptr<!void> 
-> !cir.ptr<!void>
+    // CHECK: "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %{{.+}}) <{isVolatile = 
false}> : (!llvm.ptr, !llvm.ptr, i64) -> ()
+    cir.return
+  }
+}

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

Reply via email to