Author: Ayokunle Amodu Date: 2026-03-09T17:59:00-06:00 New Revision: 4095ac92b454393c4a749d2f29565c2fc20af6f6
URL: https://github.com/llvm/llvm-project/commit/4095ac92b454393c4a749d2f29565c2fc20af6f6 DIFF: https://github.com/llvm/llvm-project/commit/4095ac92b454393c4a749d2f29565c2fc20af6f6.diff LOG: [CIR][CIRGen] Upstream support for `__builtin_bcopy` (#185038) This adds CIR support for the bcopy builtin. Added: clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp Modified: clang/include/clang/CIR/Dialect/IR/CIROps.td clang/lib/CIR/CodeGen/CIRGenBuilder.h clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp clang/lib/CIR/CodeGen/CIRGenCall.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 5a83e439a80a9..2c109eaeb392e 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3979,7 +3979,35 @@ def CIR_MemCpyOp : CIR_MemOp<"libc.memcpy"> { }]; } -// TODO: MemMoveOp +def CIR_MemMoveOp : CIR_MemOp<"libc.memmove"> { + let summary = "Equivalent to libc's `memmove`"; + let description = [{ + Given two CIR pointers, `src` and `dst`, `cir.libc.memmove` will copy `len` + bytes from the memory pointed by `src` to the memory pointed by `dst`. + + similiar to `cir.libc.memcpy` but accounts for overlapping memory. + + Examples: + + ```mlir + // Copying 2 bytes from one array to a record: + %2 = cir.const #cir.int<2> : !u32i + cir.libc.memmove %2 bytes from %arr to %record : !cir.ptr<!void>, !u64i + ``` + }]; + + let arguments = !con(commonArgs, (ins CIR_AnyFundamentalUIntType:$len)); + + let assemblyFormat = [{ + $len `bytes` `from` $src `to` $dst attr-dict + `:` qualified(type($dst)) `,` type($len) + }]; + + let extraClassDeclaration = [{ + /// Returns the byte length type. + cir::IntType getLenTy() { return getLen().getType(); } + }]; +} //===----------------------------------------------------------------------===// // MemSetOp diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index e2f89cc5aa12f..ad0bf7fdcf6b6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -240,6 +240,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return cir::MemCpyOp::create(*this, loc, dst, src, len); } + 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::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst, mlir::Value val, mlir::Value len) { assert(val.getType() == getUInt8Ty()); diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 244979aac917e..e9a8768e8213f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -1589,8 +1589,18 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, return RValue::getIgnored(); } case Builtin::BIbcopy: - case Builtin::BI__builtin_bcopy: - return errorBuiltinNYI(*this, e, builtinID); + case Builtin::BI__builtin_bcopy: { + Address src = emitPointerWithAlignment(e->getArg(0)); + Address dest = emitPointerWithAlignment(e->getArg(1)); + mlir::Value sizeVal = emitScalarExpr(e->getArg(2)); + emitNonNullArgCheck(RValue::get(src.getPointer()), e->getArg(0)->getType(), + 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); + return RValue::get(nullptr); + } case Builtin::BI__builtin_char_memchr: case Builtin::BI__builtin_memchr: { Address srcPtr = emitPointerWithAlignment(e->getArg(0)); diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 61ccd85cd6342..1d7f15c569798 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -1031,6 +1031,15 @@ RValue CallArg::getRValue(CIRGenFunction &cgf, mlir::Location loc) const { return RValue::getAggregate(copy.getAddress()); } +void CIRGenFunction::emitNonNullArgCheck(RValue rv, QualType argType, + SourceLocation argLoc, + AbstractCallee ac, unsigned paramNum) { + if (!ac.getDecl() || !(sanOpts.has(SanitizerKind::NonnullAttribute) || + sanOpts.has(SanitizerKind::NullabilityArg))) + return; + cgm.errorNYI("non-null arg check is NYI"); +} + static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 0e82958ef6f39..1b3518616d6b7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -487,6 +487,8 @@ class CIRGenFunction : public CIRGenTypeCache { return llvm::isa_and_nonnull<clang::FunctionDecl>(calleeDecl); } + const clang::Decl *getDecl() const { return calleeDecl; } + unsigned getNumParams() const { if (const auto *fd = llvm::dyn_cast<clang::FunctionDecl>(calleeDecl)) return fd->getNumParams(); @@ -1578,6 +1580,11 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Value numElements, mlir::Value allocSizeWithoutCookie); + /// Create a check for a function parameter that may potentially be + /// declared as non-null. + void emitNonNullArgCheck(RValue rv, QualType argType, SourceLocation argLoc, + AbstractCallee ac, unsigned paramNum); + RValue emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e, const CXXMethodDecl *md, ReturnValueSlot returnValue); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 8bcd040382ab3..b996a05782370 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -202,6 +202,15 @@ mlir::LogicalResult CIRToLLVMMemCpyOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMMemMoveOpLowering::matchAndRewrite( + cir::MemMoveOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + rewriter.replaceOpWithNewOp<mlir::LLVM::MemmoveOp>( + op, adaptor.getDst(), adaptor.getSrc(), adaptor.getLen(), + /*isVolatile=*/false); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMMemSetOpLowering::matchAndRewrite( cir::MemSetOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp new file mode 100644 index 0000000000000..ab8de64718014 --- /dev/null +++ b/clang/test/CIR/CodeGenBuiltins/builtin-bcopy.cpp @@ -0,0 +1,116 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +void foo(void) { + // CIR-LABEL: cir.func no_inline dso_local @_Z3foov() + // CIR: %[[V0:.*]] = cir.alloca !cir.array<!cir.float x 4>, !cir.ptr<!cir.array<!cir.float x 4>>, ["f4"] {alignment = 16 : i64} + // CIR: %[[V1:.*]] = cir.alloca !cir.array<!cir.float x 8>, !cir.ptr<!cir.array<!cir.float x 8>>, ["f8"] {alignment = 16 : i64} + // CIR: %[[V2:.*]] = cir.cast array_to_ptrdecay %[[V0]] : !cir.ptr<!cir.array<!cir.float x 4>> -> !cir.ptr<!cir.float> + // CIR: %[[V3:.*]] = cir.cast bitcast %[[V2]] : !cir.ptr<!cir.float> -> !cir.ptr<!void> + // CIR: %[[V4:.*]] = cir.cast array_to_ptrdecay %[[V1]] : !cir.ptr<!cir.array<!cir.float x 8>> -> !cir.ptr<!cir.float> + // CIR: %[[V5:.*]] = cir.cast bitcast %[[V4]] : !cir.ptr<!cir.float> -> !cir.ptr<!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.return + + // LLVM-LABEL: define dso_local void @_Z3foov() + // LLVM: %[[V1:.*]] = alloca [4 x float], i64 1, align 16 + // LLVM: %[[V2:.*]] = alloca [8 x float], i64 1, 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: ret void + + // OGCG-LABEL: define dso_local void @_Z3foov() + // OGCG: %[[V1:.*]] = alloca [4 x float], align 16 + // OGCG: %[[V2:.*]] = alloca [8 x float], align 16 + // OGCG: %[[V3:.*]] = getelementptr inbounds [4 x float], ptr %[[V1]], i64 0, i64 0 + // OGCG: %[[V4:.*]] = getelementptr inbounds [8 x float], ptr %[[V2]], i64 0, i64 0 + // OGCG: call void @llvm.memmove.p0.p0.i64(ptr align 16 %[[V4]], ptr align 16 %[[V3]], i64 16, i1 false) + // OGCG: ret void + + float f4[4]; + float f8[8]; + __builtin_bcopy(f4, f8, sizeof(float) * 4); +} + +void test_conditional_bcopy(void) { + // CIR-LABEL: cir.func {{.*}} @_Z22test_conditional_bcopyv() + // CIR: cir.ternary + // CIR: cir.ternary + // CIR: cir.libc.memmove {{.*}} bytes from {{.*}} to {{.*}} : !cir.ptr<!void>, !u64i + // CIR: false + // CIR: false + // CIR: cir.libc.memmove {{.*}} bytes from {{.*}} to {{.*}} : !cir.ptr<!void>, !u64i + + // LLVM-LABEL: define{{.*}} void @_Z22test_conditional_bcopyv + // LLVM: br + // LLVM: call void @llvm.memmove + // LLVM: br + // LLVM: call void @llvm.memmove + // LLVM-NOT: phi + + // OGCG-LABEL: define{{.*}} void @_Z22test_conditional_bcopyv + // LLVM: br + // OGCG: call void @llvm.memmove + // LLVM: br + // OGCG: call void @llvm.memmove + // OGCG-NOT: phi + + char dst[20]; + char src[20]; + int _sz = 20, len = 20; + return (_sz ? ((_sz >= len) ? __builtin_bcopy(src, dst, len) : foo()) + : __builtin_bcopy(src, dst, len)); +} + +void another_conditional_bcopy(char *dst, char *src, int sz, int len) { + // CIR-LABEL: cir.func no_inline dso_local @_Z25another_conditional_bcopyPcS_ii + // CIR: cir.if + // CIR: cir.libc.memmove {{.*}} bytes from {{.*}} to {{.*}} : !cir.ptr<!void>, !u64i + // cir: else + // CIR: cir.libc.memmove {{.*}} bytes from {{.*}} to {{.*}} : !cir.ptr<!void>, !u64i + + // LLVM-LABEL: define{{.*}} void @_Z25another_conditional_bcopyPcS_ii + // LLVM: br + // LLVM: call void @llvm.memmove + // LLVM: br + // LLVM: call void @llvm.memmove + // LLVM-NOT: phi + + // OGCG-LABEL: define{{.*}} void @_Z25another_conditional_bcopyPcS_ii + // OGCG: br + // OGCG: call void @llvm.memmove + // OGCG: br + // OGCG: call void @llvm.memmove + // OGCG-NOT: phi + + if (sz >= len) + __builtin_bcopy(src, dst, len); + else + __builtin_bcopy(src, dst, len * 2); +} + +#define size_t __SIZE_TYPE__ + +extern "C" void bcopy(const void *__src, void *__dest, size_t __n); +// CIR: @_Z9testbcopyPKvPvm( +// CIR: cir.libc.memmove {{.*}} bytes from {{.*}} to {{.*}} : !cir.ptr<!void>, !u64i +// CIR: cir.return + +// LLVM: @_Z9testbcopyPKvPvm( +// LLVM: call void @llvm.memmove.p0.p0.i64(ptr {{.*}}, ptr {{.*}}, i64 {{.*}}, i1 false) +// LLVM: ret void + +// OGCG: @_Z9testbcopyPKvPvm( +// OGCG: call void @llvm.memmove.p0.p0.i64(ptr {{.*}}, ptr {{.*}}, i64 {{.*}}, i1 false) +// OGCG: ret void +void testbcopy(const void *src, void *dest, size_t n) { + bcopy(src, dest, n); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
