diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2226,6 +2226,7 @@ Clang provides constant expression evaluation support for builtins forms of the following functions from the C standard library ```` header: +* ``memcpy`` * ``memchr`` * ``memcmp`` * ``strchr`` @@ -2246,7 +2247,22 @@ void *p = __builtin_memchr("foobar", 'b', 5); -In addition to the above, one further builtin is provided: +In addition to the above, the following builtins are provided: + + * ``__builtin_memcpy_inline`` + +.. code-block:: c + + void __builtin_memcpy_inline(void *dst, const void *src, size_t size); + +``__builtin_memcpy_inline(dst, src, size)`` is identical to +``__builtin_memcpy(dst, src, size)`` except that the generated code is +guaranteed not to call any external functions. See [LLVM IR ‘llvm.memcpy.inline’ +Intrinsic](https://llvm.org/docs/LangRef.html#llvm-memcpy-inline-intrinsic) for +more information. + + + * ``__builtin_char_memchr`` .. code-block:: c diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -480,6 +480,7 @@ BUILTIN(__builtin_memchr, "v*vC*iz", "nF") BUILTIN(__builtin_memcmp, "ivC*vC*z", "nF") BUILTIN(__builtin_memcpy, "v*v*vC*z", "nF") +BUILTIN(__builtin_memcpy_inline, "vv*vC*z", "nt") BUILTIN(__builtin_memmove, "v*v*vC*z", "nF") BUILTIN(__builtin_mempcpy, "v*v*vC*z", "nF") BUILTIN(__builtin_memset, "v*v*iz", "nF") diff --git a/clang/lib/CodeGen/CGBuilder.h b/clang/lib/CodeGen/CGBuilder.h --- a/clang/lib/CodeGen/CGBuilder.h +++ b/clang/lib/CodeGen/CGBuilder.h @@ -280,6 +280,13 @@ IsVolatile); } + using CGBuilderBaseTy::CreateMemCpyInline; + llvm::CallInst *CreateMemCpyInline(Address Dest, Address Src, uint64_t Size) { + return CreateMemCpyInline( + Dest.getPointer(), Dest.getAlignment().getAsAlign(), Src.getPointer(), + Src.getAlignment().getAsAlign(), getInt64(Size)); + } + using CGBuilderBaseTy::CreateMemMove; llvm::CallInst *CreateMemMove(Address Dest, Address Src, llvm::Value *Size, bool IsVolatile = false) { diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2518,6 +2518,19 @@ return RValue::get(Dest.getPointer()); } + case Builtin::BI__builtin_memcpy_inline: { + Address Dest = EmitPointerWithAlignment(E->getArg(0)); + Address Src = EmitPointerWithAlignment(E->getArg(1)); + uint64_t Size = + E->getArg(2)->EvaluateKnownConstInt(getContext()).getZExtValue(); + 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); + Builder.CreateMemCpyInline(Dest, Src, Size); + return RValue::get(nullptr); + } + case Builtin::BI__builtin_char_memchr: BuiltinID = Builtin::BI__builtin_memchr; break; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1376,6 +1376,9 @@ return true; } +static void CheckNonNullArgument(Sema &S, const Expr *ArgExpr, + SourceLocation CallSiteLoc); + ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall) { @@ -1645,6 +1648,17 @@ case Builtin::BI__builtin_nontemporal_load: case Builtin::BI__builtin_nontemporal_store: return SemaBuiltinNontemporalOverloaded(TheCallResult); + case Builtin::BI__builtin_memcpy_inline: { + llvm::APSInt I(64); + if (!TheCall->getArg(2)->isIntegerConstantExpr(I, Context)) + Diag(TheCall->getBeginLoc(), diag::err_expr_not_ice) + << LangOpts.CPlusPlus << TheCall->getArg(2)->getSourceRange(); + if (I.isNullValue()) + break; + CheckNonNullArgument(*this, TheCall->getArg(0), TheCall->getExprLoc()); + CheckNonNullArgument(*this, TheCall->getArg(1), TheCall->getExprLoc()); + break; + } #define BUILTIN(ID, TYPE, ATTRS) #define ATOMIC_BUILTIN(ID, TYPE, ATTRS) \ case Builtin::BI##ID: \ diff --git a/clang/test/CodeGen/builtins-memcpy-inline.c b/clang/test/CodeGen/builtins-memcpy-inline.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtins-memcpy-inline.c @@ -0,0 +1,26 @@ +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s + +// CHECK-LABEL: define void @test_memcpy_inline_0(i8* %dst, i8* %src) +void test_memcpy_inline_0(void *dst, const void *src) { + // CHECK: call void @llvm.memcpy.inline.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 %1, i64 0, i1 false) + __builtin_memcpy_inline(dst, src, 0); +} + +// CHECK-LABEL: define void @test_memcpy_inline_1(i8* %dst, i8* %src) +void test_memcpy_inline_1(void *dst, const void *src) { + // CHECK: call void @llvm.memcpy.inline.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 %1, i64 1, i1 false) + __builtin_memcpy_inline(dst, src, 1); +} + +// CHECK-LABEL: define void @test_memcpy_inline_4(i8* %dst, i8* %src) +void test_memcpy_inline_4(void *dst, const void *src) { + // CHECK: call void @llvm.memcpy.inline.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 %1, i64 4, i1 false) + __builtin_memcpy_inline(dst, src, 4); +} + +// CHECK-LABEL: define void @test_memcpy_inline_aligned_buffers(i64* %dst, i64* %src) +void test_memcpy_inline_aligned_buffers(unsigned long long *dst, const unsigned long long *src) { + // CHECK: call void @llvm.memcpy.inline.p0i8.p0i8.i64(i8* align 8 %2, i8* align 8 %3, i64 4, i1 false) + __builtin_memcpy_inline(dst, src, 4); +} diff --git a/clang/test/Sema/builtins-memcpy-inline.c b/clang/test/Sema/builtins-memcpy-inline.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/builtins-memcpy-inline.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define NULL ((char *)0) + +#if __has_feature(__builtin_memcpy_inline) +#warning defined as expected +// expected-warning@-1 {{defined as expected}} +#endif + +void test_memcpy_inline_null_src(void *ptr) { + __builtin_memcpy_inline(ptr, NULL, 4); // expected-warning {{null passed to a callee that requires a non-null argument}} +} + +void test_memcpy_inline_null_dst(void *ptr) { + __builtin_memcpy_inline(NULL, ptr, 4); // expected-warning {{null passed to a callee that requires a non-null argument}} +} + +void test_memcpy_inline_null_buffers() { + __builtin_memcpy_inline(NULL, NULL, 4); + // expected-warning@-1 {{null passed to a callee that requires a non-null argument}} + // expected-warning@-2 {{null passed to a callee that requires a non-null argument}} +} + +void test_memcpy_inline_null_buffer_is_ok_if_size_is_zero(void *ptr) { + __builtin_memcpy_inline(ptr, NULL, /*size */ 0); + __builtin_memcpy_inline(NULL, ptr, /*size */ 0); + __builtin_memcpy_inline(NULL, NULL, /*size */ 0); +} + +void test_memcpy_inline_non_constant_size(void *dst, const void *src, unsigned size) { + __builtin_memcpy_inline(dst, src, size); // expected-error {{expression is not an integer constant expression}} +} diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -560,6 +560,9 @@ MDNode *ScopeTag = nullptr, MDNode *NoAliasTag = nullptr); + CallInst *CreateMemCpyInline(Value *Dst, MaybeAlign DstAlign, Value *Src, + MaybeAlign SrcAlign, Value *Size); + /// Create and insert an element unordered-atomic memcpy between the /// specified pointers. /// diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp --- a/llvm/lib/IR/IRBuilder.cpp +++ b/llvm/lib/IR/IRBuilder.cpp @@ -200,6 +200,30 @@ return CI; } +CallInst *IRBuilderBase::CreateMemCpyInline(Value *Dst, MaybeAlign DstAlign, + Value *Src, MaybeAlign SrcAlign, + Value *Size) { + Dst = getCastedInt8PtrValue(Dst); + Src = getCastedInt8PtrValue(Src); + Value *IsVolatile = getInt1(false); + + Value *Ops[] = {Dst, Src, Size, IsVolatile}; + Type *Tys[] = {Dst->getType(), Src->getType(), Size->getType()}; + Function *F = BB->getParent(); + Module *M = F->getParent(); + Function *TheFn = Intrinsic::getDeclaration(M, Intrinsic::memcpy_inline, Tys); + + CallInst *CI = createCallHelper(TheFn, Ops, this); + + auto *MCI = cast(CI); + if (DstAlign) + MCI->setDestAlignment(*DstAlign); + if (SrcAlign) + MCI->setSourceAlignment(*SrcAlign); + + return CI; +} + CallInst *IRBuilderBase::CreateElementUnorderedAtomicMemCpy( Value *Dst, Align DstAlign, Value *Src, Align SrcAlign, Value *Size, uint32_t ElementSize, MDNode *TBAATag, MDNode *TBAAStructTag,