[clang] Add support for __builtin_memcpy_inline

Summary: This is a follow up on D61634 and the last step to implement http://lists.llvm.org/pipermail/llvm-dev/2019-April/131973.html

Reviewers: efriedma, courbet, tejohnson

Subscribers: hiraditya, cfe-commits, llvm-commits, jdoerfert, t.p.northover

Tags: #clang, #llvm

Differential Revision: https://reviews.llvm.org/D73543
This commit is contained in:
Guillaume Chatelet
2020-02-07 23:55:26 +01:00
parent fafc6e4fdf
commit d65bbf81f8
9 changed files with 134 additions and 0 deletions
+17
View File
@@ -2260,6 +2260,23 @@ is disallowed in general).
Support for constant expression evaluation for the above builtins be detected
with ``__has_feature(cxx_constexpr_string_builtins)``.
Memory builtins
---------------
* ``__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.
Note that the `size` argument must be a compile time constant.
Atomic Min/Max builtins with memory ordering
--------------------------------------------
+1
View File
@@ -480,6 +480,7 @@ BUILTIN(__builtin_fprintf, "iP*cC*.", "Fp:1:")
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*Iz", "nt")
BUILTIN(__builtin_memmove, "v*v*vC*z", "nF")
BUILTIN(__builtin_mempcpy, "v*v*vC*z", "nF")
BUILTIN(__builtin_memset, "v*v*iz", "nF")
+7
View File
@@ -280,6 +280,13 @@ public:
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) {
+13
View File
@@ -2518,6 +2518,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
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;
+11
View File
@@ -1376,6 +1376,9 @@ CheckBuiltinTargetSupport(Sema &S, unsigned BuiltinID, CallExpr *TheCall,
return true;
}
static void CheckNonNullArgument(Sema &S, const Expr *ArgExpr,
SourceLocation CallSiteLoc);
ExprResult
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
CallExpr *TheCall) {
@@ -1645,6 +1648,14 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
case Builtin::BI__builtin_nontemporal_load:
case Builtin::BI__builtin_nontemporal_store:
return SemaBuiltinNontemporalOverloaded(TheCallResult);
case Builtin::BI__builtin_memcpy_inline: {
// __builtin_memcpy_inline size argument is a constant by definition.
if (TheCall->getArg(2)->EvaluateKnownConstInt(Context).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: \
@@ -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);
}
+32
View File
@@ -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 {{argument to '__builtin_memcpy_inline' must be a constant integer}}
}
+3
View File
@@ -560,6 +560,9 @@ public:
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.
///
+24
View File
@@ -200,6 +200,30 @@ CallInst *IRBuilderBase::CreateMemCpy(Value *Dst, MaybeAlign DstAlign,
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<MemCpyInlineInst>(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,