mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 09:06:21 +01:00
[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:
@@ -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
|
||||
--------------------------------------------
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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}}
|
||||
}
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user