[clang] [CIR] Emit cir.libc.memmove and cir.libc.memset for memmove and memset (PR #227667)
Steffen Larsen via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 30 05:20:43 PDT 2026
https://github.com/steffenlarsen updated https://github.com/llvm/llvm-project/pull/227667
>From 7943ab852ebde7a223a8303a25c105c31019b177 Mon Sep 17 00:00:00 2001
From: Steffen Holst Larsen <sholstla at amd.com>
Date: Wed, 23 Sep 2026 00:16:15 -0500
Subject: [PATCH] [CIR] Emit cir.libc.memmove and cir.libc.memset for memmove
and memset
memmove, memset and their __builtin_ forms went through calls to
memmove and memset where classic codegen emits the llvm.memmove and
llvm.memset intrinsics. Emit cir.libc.memmove and cir.libc.memset
instead, following CGBuiltin.cpp and the existing memcpy handling. They
lower to the same intrinsics. __builtin_trivially_relocate shares the
memmove path and, as in classic codegen, scales its count by the element
size.
Additionally, this adds alignment to the cir.libc.memmove Op (similar to
memcpy in #225845) and properly sets the alignment for both
cir.libc.memmove and cir.libc.memset.
Note that emitDirectCallee still ignores __attribute__((no_builtin)), so
calls to these functions from a no_builtin function now become
intrinsics where classic codegen keeps the library call. This should be
addressed separately as part of the no_builtin support.
Assisted-by: Claude Opus 5.5
Signed-off-by: Steffen Holst Larsen <sholstla at amd.com>
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 9 ++-
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 9 ++-
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 44 +++++++++++---
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 17 +++++-
clang/test/CIR/CodeGen/libc.c | 58 ++++++++++++++-----
.../CIR/CodeGenBuiltins/builtin-bcopy.cpp | 8 +--
.../test/CIR/CodeGenBuiltins/builtin-inline.c | 28 ++-------
.../builtin-trivially-relocate.cpp | 38 ++++++++++++
clang/test/CIR/IR/libc-memmove.cir | 12 ++++
9 files changed, 169 insertions(+), 54 deletions(-)
create mode 100644 clang/test/CIR/CodeGenBuiltins/builtin-trivially-relocate.cpp
create mode 100644 clang/test/CIR/IR/libc-memmove.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index de805d4c642a2..43e1f3433a41d 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5307,10 +5307,15 @@ def CIR_MemMoveOp : CIR_MemOp<"libc.memmove"> {
```
}];
- let arguments = !con(commonArgs, (ins CIR_AnyFundamentalUIntType:$len));
+ let arguments = !con(commonArgs, (ins
+ CIR_AnyFundamentalUIntType:$len,
+ OptionalAttr<I64Attr>:$dst_alignment,
+ OptionalAttr<I64Attr>:$src_alignment
+ ));
let assemblyFormat = [{
- $len `bytes` `from` $src `to` $dst attr-dict
+ $len `bytes` `from` $src (`align` `(` $src_alignment^ `)`)?
+ `to` $dst (`align` `(` $dst_alignment^ `)`)? attr-dict
`:` qualified(type($dst)) `,` type($len)
}];
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index b581212b0db56..cb350f220d611 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -203,9 +203,12 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
getI64IntegerAttr(src.getAlignment().getQuantity()));
}
- cir::MemMoveOp createMemMove(mlir::Location loc, mlir::Value dst,
- mlir::Value src, mlir::Value len) {
- return cir::MemMoveOp::create(*this, loc, dst, src, len);
+ cir::MemMoveOp createMemMove(mlir::Location loc, Address dst, Address src,
+ mlir::Value len) {
+ return cir::MemMoveOp::create(
+ *this, loc, dst.getPointer(), src.getPointer(), len,
+ getI64IntegerAttr(dst.getAlignment().getQuantity()),
+ getI64IntegerAttr(src.getAlignment().getQuantity()));
}
cir::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst,
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 245708691b7d9..14e47ca3854d8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2412,8 +2412,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
e->getArg(0)->getExprLoc(), fd, 0);
emitNonNullArgCheck(RValue::get(dest.getPointer()), e->getArg(1)->getType(),
e->getArg(1)->getExprLoc(), fd, 0);
- builder.createMemMove(getLoc(e->getSourceRange()), dest.getPointer(),
- src.getPointer(), sizeVal);
+ builder.createMemMove(getLoc(e->getSourceRange()), dest, src, sizeVal);
return RValue::get(nullptr);
}
case Builtin::BI__builtin_char_memchr:
@@ -2448,15 +2447,46 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
}
return RValue::get(dest.getPointer());
}
+ case Builtin::BI__builtin_trivially_relocate:
+ case Builtin::BImemmove:
+ case Builtin::BI__builtin_memmove: {
+ mlir::Location loc = getLoc(e->getSourceRange());
+ Address dest = emitPointerWithAlignment(e->getArg(0));
+ Address src = emitPointerWithAlignment(e->getArg(1));
+ mlir::Value sizeVal = emitScalarExpr(e->getArg(2));
+ if (builtinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) {
+ QualType eltTy = e->getArg(0)->getType()->getPointeeType();
+ CharUnits eltSize = getContext().getTypeSizeInChars(eltTy);
+ mlir::Value eltSizeVal =
+ builder.getConstInt(loc, sizeVal.getType(), eltSize.getQuantity());
+ sizeVal = builder.createMul(loc, sizeVal, eltSizeVal);
+ }
+ emitNonNullArgCheck(RValue::get(dest.getPointer()), e->getArg(0)->getType(),
+ e->getArg(0)->getExprLoc(), fd, 0);
+ emitNonNullArgCheck(RValue::get(src.getPointer()), e->getArg(1)->getType(),
+ e->getArg(1)->getExprLoc(), fd, 1);
+ assert(!cir::MissingFeatures::sanitizers());
+ builder.createMemMove(loc, dest.withElementType(builder, cgm.voidTy),
+ src.withElementType(builder, cgm.voidTy), sizeVal);
+ assert(!cir::MissingFeatures::generateDebugInfo());
+ return RValue::get(dest.getPointer());
+ }
+ case Builtin::BImemset:
+ case Builtin::BI__builtin_memset: {
+ Address dest = emitPointerWithAlignment(e->getArg(0));
+ mlir::Value byteVal = builder.createIntCast(emitScalarExpr(e->getArg(1)),
+ builder.getUInt8Ty());
+ mlir::Value sizeVal = emitScalarExpr(e->getArg(2));
+ emitNonNullArgCheck(RValue::get(dest.getPointer()), e->getArg(0)->getType(),
+ e->getArg(0)->getExprLoc(), fd, 0);
+ builder.createMemSet(getLoc(e->getSourceRange()), dest, byteVal, sizeVal);
+ assert(!cir::MissingFeatures::generateDebugInfo());
+ return RValue::get(dest.getPointer());
+ }
case Builtin::BI__builtin_memcpy_inline:
case Builtin::BI__builtin___memcpy_chk:
case Builtin::BI__builtin_objc_memmove_collectable:
case Builtin::BI__builtin___memmove_chk:
- case Builtin::BI__builtin_trivially_relocate:
- case Builtin::BImemmove:
- case Builtin::BI__builtin_memmove:
- case Builtin::BImemset:
- case Builtin::BI__builtin_memset:
case Builtin::BI__builtin_memset_inline:
case Builtin::BI__builtin___memset_chk:
case Builtin::BI__builtin_wmemchr:
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 4d2a99d5015fe..3b498a4007c17 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -349,9 +349,24 @@ mlir::LogicalResult CIRToLLVMMemCpyOpLowering::matchAndRewrite(
mlir::LogicalResult CIRToLLVMMemMoveOpLowering::matchAndRewrite(
cir::MemMoveOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::ArrayAttr argAttrs;
+ if (op.getDstAlignment() || op.getSrcAlignment()) {
+ mlir::NamedAttribute dstAlignAttr = rewriter.getNamedAttr(
+ mlir::LLVM::LLVMDialect::getAlignAttrName(),
+ rewriter.getI64IntegerAttr(op.getDstAlignment().value_or(1)));
+ mlir::NamedAttribute srcAlignAttr = rewriter.getNamedAttr(
+ mlir::LLVM::LLVMDialect::getAlignAttrName(),
+ rewriter.getI64IntegerAttr(op.getSrcAlignment().value_or(1)));
+ argAttrs = rewriter.getArrayAttr({
+ /*dst_attrs=*/rewriter.getDictionaryAttr({dstAlignAttr}),
+ /*src_attrs=*/rewriter.getDictionaryAttr({srcAlignAttr}),
+ });
+ }
rewriter.replaceOpWithNewOp<mlir::LLVM::MemmoveOp>(
op, adaptor.getDst(), adaptor.getSrc(), adaptor.getLen(),
- /*isVolatile=*/false);
+ /*isVolatile=*/false, /*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
+ /*noalias_scopes=*/nullptr, /*tbaa=*/nullptr, /*arg_attrs=*/argAttrs,
+ /*res_attrs=*/nullptr);
return mlir::success();
}
diff --git a/clang/test/CIR/CodeGen/libc.c b/clang/test/CIR/CodeGen/libc.c
index 1ea383b1559d2..afcb5ac013be4 100644
--- a/clang/test/CIR/CodeGen/libc.c
+++ b/clang/test/CIR/CodeGen/libc.c
@@ -3,19 +3,14 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t-ogcg.ll
-// RUN: FileCheck --check-prefix=OGCG --input-file=%t-ogcg.ll %s
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-ogcg.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -fwrapv
// RUN: FileCheck --check-prefix=CIR_NO_POISON --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -fwrapv
// RUN: FileCheck --check-prefix=LLVM_NO_POISON --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t-ogcg-wrapv.ll -fwrapv
-// RUN: FileCheck --check-prefix=OGCG_NO_POISON --input-file=%t-ogcg-wrapv.ll %s
-
-// Note: In the final implementation, we will want these to generate
-// CIR-specific libc operations. This test is just a placeholder
-// to make sure we can compile these to normal function calls
-// until the special handling is implemented.
+// RUN: FileCheck --check-prefix=LLVM_NO_POISON --input-file=%t-ogcg-wrapv.ll %s
void *memcpy(void *, const void *, unsigned long);
void testMemcpy(void *dst, const void *src, unsigned long size) {
@@ -33,13 +28,52 @@ void *testMempcpy(void *dst, const void *src, unsigned long size) {
void *memmove(void *, const void *, unsigned long);
void testMemmove(void *src, const void *dst, unsigned long size) {
memmove(dst, src, size);
- // CHECK: cir.call @memmove
+ // CHECK: cir.libc.memmove %{{.+}} bytes from %{{.+}} to %{{.+}} : !cir.ptr<!void>, !u64i
+ // LLVM: call void @llvm.memmove.p0.p0.i64(ptr {{.*}}, ptr {{.*}}, i64 {{.*}}, i1 false)
}
void *memset(void *, int, unsigned long);
void testMemset(void *dst, int val, unsigned long size) {
memset(dst, val, size);
- // CHECK: cir.call @memset
+ // CHECK: cir.libc.memset %{{.+}} bytes at %{{.+}} to %{{.+}} : !cir.ptr<!void>, !u8i, !u64i
+ // LLVM: call void @llvm.memset.p0.i64(ptr {{.*}}, i8 {{.*}}, i64 {{.*}}, i1 false)
+}
+
+void *testBuiltinMemmove(void *dst, const void *src, unsigned long size) {
+ return __builtin_memmove(dst, src, size);
+ // CHECK-LABEL: cir.func {{.*}} @testBuiltinMemmove(
+ // CHECK: %[[DST:.+]] = cir.load {{.*}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ // CHECK: cir.libc.memmove %{{.+}} bytes from %{{.+}} align(1) to %[[DST]] align(1) : !cir.ptr<!void>, !u64i
+ // CHECK: cir.store %[[DST]], %{{.+}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ // LLVM-LABEL: define {{.*}} ptr @testBuiltinMemmove(
+ // LLVM: call void @llvm.memmove.p0.p0.i64(ptr {{.*}}, ptr {{.*}}, i64 {{.*}}, i1 false)
+}
+
+void *testBuiltinMemset(void *dst, int val, unsigned long size) {
+ return __builtin_memset(dst, val, size);
+ // CHECK-LABEL: cir.func {{.*}} @testBuiltinMemset(
+ // CHECK: %[[DST:.+]] = cir.load {{.*}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ // CHECK: %[[VAL:.+]] = cir.load {{.*}} : !cir.ptr<!s32i>, !s32i
+ // CHECK: %[[BYTE:.+]] = cir.cast integral %[[VAL]] : !s32i -> !u8i
+ // CHECK: cir.libc.memset %{{.+}} bytes at %[[DST]] align(1) to %[[BYTE]] : !cir.ptr<!void>, !u8i, !u64i
+ // CHECK: cir.store %[[DST]], %{{.+}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+ // LLVM-LABEL: define {{.*}} ptr @testBuiltinMemset(
+ // LLVM: [[VAL:%.*]] = load i32, ptr
+ // LLVM: [[BYTE:%.*]] = trunc i32 [[VAL]] to i8
+ // LLVM: call void @llvm.memset.p0.i64(ptr {{.*}}, i8 [[BYTE]], i64 {{.*}}, i1 false)
+}
+
+// The alignment comes from the pointee type of the argument before its
+// conversion to void *.
+void testBuiltinAligned(int *dst, const int *src, unsigned long size) {
+ __builtin_memmove(dst, src, size);
+ __builtin_memset(dst, 0, size);
+ // CHECK-LABEL: cir.func {{.*}} @testBuiltinAligned(
+ // CHECK: cir.libc.memmove %{{.+}} bytes from %{{.+}} align(4) to %{{.+}} align(4) : !cir.ptr<!void>, !u64i
+ // CHECK: cir.libc.memset %{{.+}} bytes at %{{.+}} align(4) to %{{.+}} : !cir.ptr<!void>, !u8i, !u64i
+ // LLVM-LABEL: define {{.*}} void @testBuiltinAligned(
+ // LLVM: call void @llvm.memmove.p0.p0.i64(ptr align 4 %{{.+}}, ptr align 4 %{{.+}}, i64 %{{.+}}, i1 false)
+ // LLVM: call void @llvm.memset.p0.i64(ptr align 4 %{{.+}}, i8 0, i64 %{{.+}}, i1 false)
}
double fabs(double);
@@ -59,10 +93,8 @@ int testAbs(int x) {
return abs(x);
// CHECK: cir.abs %{{.+}} min_is_poison : !s32i
// LLVM: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 true)
- // OGCG: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 true)
// CIR_NO_POISON: cir.abs %{{[^ ]+}} : !s32i
// LLVM_NO_POISON: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 false)
- // OGCG_NO_POISON: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 false)
}
long labs(long);
@@ -70,10 +102,8 @@ long testLabs(long x) {
return labs(x);
// CHECK: cir.abs %{{.+}} min_is_poison : !s64i
// LLVM: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
- // OGCG: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
// CIR_NO_POISON: cir.abs %{{[^ ]+}} : !s64i
// LLVM_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
- // OGCG_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
}
long long llabs(long long);
@@ -81,8 +111,6 @@ long long testLlabs(long long x) {
return llabs(x);
// CHECK: cir.abs %{{.+}} min_is_poison : !s64i
// LLVM: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
- // OGCG: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
// CIR_NO_POISON: cir.abs %{{[^ ]+}} : !s64i
// LLVM_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
- // OGCG_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
}
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp
index 8a1243c13f7d7..2dbdb5b0d2789 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp
@@ -16,7 +16,7 @@ void foo(void) {
// CIR: %[[V6:.*]] = cir.const #cir.int<4> : !u64i
// CIR: %[[V7:.*]] = cir.const #cir.int<4> : !u64i
// CIR: %[[V8:.*]] = cir.mul %[[V6]], %[[V7]] : !u64i
- // CIR: cir.libc.memmove %[[V8]] bytes from %[[V3]] to %[[V5]] : !cir.ptr<!void>, !u64i
+ // CIR: cir.libc.memmove %[[V8]] bytes from %[[V3]] align(16) to %[[V5]] align(16) : !cir.ptr<!void>, !u64i
// CIR: cir.return
// LLVM-LABEL: define dso_local void @_Z3foov()
@@ -24,7 +24,7 @@ void foo(void) {
// LLVM: %[[V2:.*]] = alloca [8 x float], align 16
// LLVM: %[[V3:.*]] = getelementptr float, ptr %[[V1]], i32 0
// LLVM: %[[V4:.*]] = getelementptr float, ptr %[[V2]], i32 0
- // LLVM: call void @llvm.memmove.p0.p0.i64(ptr %[[V4]], ptr %[[V3]], i64 16, i1 false)
+ // LLVM: call void @llvm.memmove.p0.p0.i64(ptr align 16 %[[V4]], ptr align 16 %[[V3]], i64 16, i1 false)
// LLVM: ret void
// OGCG-LABEL: define dso_local void @_Z3foov()
@@ -120,11 +120,11 @@ void testbcopy(const void *src, void *dest, size_t n) {
// CIR: %[[DEST:.*]] = cir.alloca "dest" {{.*}} init : !cir.ptr<!cir.ptr<!s8i>>
// CIR: %[[SRC_TO_VOIDPTR:.*]] = cir.cast bitcast %[[SRC]] : !cir.ptr<!cir.ptr<!s8i>> -> !cir.ptr<!void>
// CIR: %[[DEST_TO_VOIDPTR:.*]] = cir.cast bitcast %[[DEST]] : !cir.ptr<!cir.ptr<!s8i>> -> !cir.ptr<!void>
-// CIR: cir.libc.memmove {{.*}} bytes from %[[SRC_TO_VOIDPTR]] to %[[DEST_TO_VOIDPTR]]
+// CIR: cir.libc.memmove {{.*}} bytes from %[[SRC_TO_VOIDPTR]] align(8) to %[[DEST_TO_VOIDPTR]] align(8)
// LLVM-LABEL: @testaddressof(
// LLVM: %[[SRC:.*]] = alloca ptr
// LLVM: %[[DEST:.*]] = alloca ptr
-// LLVM: call void @llvm.memmove.p0.p0.i64(ptr %[[DEST]], ptr %[[SRC]], i64 {{.*}}, i1 false)
+// LLVM: call void @llvm.memmove.p0.p0.i64(ptr align 8 %[[DEST]], ptr align 8 %[[SRC]], i64 {{.*}}, i1 false)
// OGCG-LABEL: @testaddressof(
// OGCG: %[[SRC:.*]] = alloca ptr
// OGCG: %[[DEST:.*]] = alloca ptr
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-inline.c b/clang/test/CIR/CodeGenBuiltins/builtin-inline.c
index 06437ecd6ccd6..f48d4bfc076bb 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-inline.c
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-inline.c
@@ -3,7 +3,7 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -disable-llvm-passes %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
typedef unsigned long size_t;
@@ -26,15 +26,9 @@ void *test_inline_builtin_memcpy(void *a, const void *b, size_t c) {
// CIR: cir.call @memcpy.inline(
// CIR: }
-// LLVM: define internal ptr @memcpy.inline(ptr{{.*}}, ptr{{.*}}, i64{{.*}}) #{{[0-9]+}}
-
-// LLVM-LABEL: @test_inline_builtin_memcpy(
-// LLVM: call ptr @memcpy.inline(
-
-// OGCG-LABEL: @test_inline_builtin_memcpy(
-// OGCG: call ptr @memcpy.inline(
-
-// OGCG: define internal ptr @memcpy.inline(ptr{{.*}} %a, ptr{{.*}} %b, i64{{.*}} %c) #{{[0-9]+}}
+// CIR emits the .inline clone before its caller, OGCG after it.
+// LLVM-DAG: define internal ptr @memcpy.inline(ptr{{.*}}, ptr{{.*}}, i64{{.*}}) #{{[0-9]+}}
+// LLVM-DAG: call ptr @memcpy.inline(ptr noundef
// Shadowing case
// When a non-inline function definition shadows an inline builtin declaration,
@@ -68,7 +62,7 @@ void *test_shadowed_memmove(void *a, const void *b, size_t c) {
// CIR-NOT: @memmove.inline
// CIR-LABEL: @test_shadowed_memmove(
-// CIR: cir.call @memmove(
+// CIR: cir.libc.memmove
// CIR-NOT: @memmove.inline
// CIR: }
@@ -76,16 +70,6 @@ void *test_shadowed_memmove(void *a, const void *b, size_t c) {
// LLVM-NOT: @memmove.inline
// LLVM-LABEL: @test_shadowed_memmove(
-// TODO - this deviation from OGCG is expected until we implement the nobuiltin
-// attribute. See CIRGenFunction::emitDirectCallee
-// LLVM: call ptr @memmove(
+// LLVM: call void @llvm.memmove.p0.p0.i64(
// LLVM-NOT: @memmove.inline
// LLVM: }
-
-// OGCG: define dso_local ptr @memmove(ptr{{.*}} %a, ptr{{.*}} %b, i64{{.*}} %c) #{{[0-9]+}}
-// OGCG-NOT: @memmove.inline
-
-// OGCG-LABEL: @test_shadowed_memmove(
-// OGCG: call void @llvm.memmove.p0.p0.i64(
-// OGCG-NOT: @memmove.inline
-// OGCG: }
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-trivially-relocate.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-trivially-relocate.cpp
new file mode 100644
index 0000000000000..c49e1ff6caf37
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-trivially-relocate.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - \
+// RUN: | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - \
+// RUN: | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - \
+// RUN: | FileCheck %s --check-prefix=LLVM
+
+typedef __SIZE_TYPE__ size_t;
+
+struct S {
+ int a;
+ int b;
+};
+
+// The count is in elements, so it is scaled by sizeof(S) before the memmove.
+S *test(S *source, S *dest, size_t count) {
+ return __builtin_trivially_relocate(dest, source, count);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z4testP1SS0_m(
+// CIR: %[[SOURCE_ADDR:.*]] = cir.alloca "source"
+// CIR: %[[DEST_ADDR:.*]] = cir.alloca "dest"
+// CIR: %[[DEST:.*]] = cir.load {{.*}} %[[DEST_ADDR]]
+// CIR: %[[SOURCE:.*]] = cir.load {{.*}} %[[SOURCE_ADDR]]
+// CIR: %[[COUNT:.*]] = cir.load {{.*}} : !cir.ptr<!u64i>, !u64i
+// CIR: %[[ELT_SIZE:.*]] = cir.const #cir.int<8> : !u64i
+// CIR: %[[SIZE:.*]] = cir.mul %[[COUNT]], %[[ELT_SIZE]] : !u64i
+// CIR: %[[SOURCE_VOID:.*]] = cir.cast bitcast %[[SOURCE]] : !cir.ptr<!rec_S> -> !cir.ptr<!void>
+// CIR: %[[DEST_VOID:.*]] = cir.cast bitcast %[[DEST]] : !cir.ptr<!rec_S> -> !cir.ptr<!void>
+// CIR: cir.libc.memmove %[[SIZE]] bytes from %[[SOURCE_VOID]] align(4) to %[[DEST_VOID]] align(4)
+// CIR: cir.store %[[DEST]], %{{.*}} : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+
+// LLVM-LABEL: define {{.*}} ptr @_Z4testP1SS0_m(
+// LLVM: [[DEST:%.*]] = load ptr, ptr
+// LLVM: [[SOURCE:%.*]] = load ptr, ptr
+// LLVM: [[COUNT:%.*]] = load i64, ptr
+// LLVM: [[SIZE:%.*]] = mul i64 [[COUNT]], 8
+// LLVM: call void @llvm.memmove.p0.p0.i64(ptr align 4 [[DEST]], ptr align 4 [[SOURCE]], i64 [[SIZE]], i1 false)
diff --git a/clang/test/CIR/IR/libc-memmove.cir b/clang/test/CIR/IR/libc-memmove.cir
new file mode 100644
index 0000000000000..a1ec6378c411a
--- /dev/null
+++ b/clang/test/CIR/IR/libc-memmove.cir
@@ -0,0 +1,12 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!u64i = !cir.int<u, 64>
+module {
+ cir.func @shouldParseLibcMemmoveOp(%arg0 : !cir.ptr<!cir.void>, %arg1 : !u64i) {
+ // CHECK: cir.libc.memmove %{{.+}} bytes from %{{.+}} to %{{.+}} : !cir.ptr<!void>, !u64i
+ cir.libc.memmove %arg1 bytes from %arg0 to %arg0 : !cir.ptr<!cir.void>, !u64i
+ // CHECK: cir.libc.memmove %{{.+}} bytes from %{{.+}} align(4) to %{{.+}} align(8) : !cir.ptr<!void>, !u64i
+ cir.libc.memmove %arg1 bytes from %arg0 align(4) to %arg0 align(8) : !cir.ptr<!cir.void>, !u64i
+ cir.return
+ }
+}
More information about the cfe-commits
mailing list