1 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
2
3 // RUN: %clang_cc1 -no-opaque-pointers -triple x86_64 -S -emit-llvm -o - %s | FileCheck %s
4 //
5 // Verifies that clang detects memcpy inline version and uses it instead of the builtin.
6
7 typedef unsigned long size_t;
8
9 // Clang requires these attributes for a function to be redefined.
10 #define AVAILABLE_EXTERNALLY extern inline __attribute__((always_inline)) __attribute__((gnu_inline))
11
12 // Clang recognizes an inline builtin and renames it to prevent conflict with builtins.
memcpy(void * a,const void * b,size_t c)13 AVAILABLE_EXTERNALLY void *memcpy(void *a, const void *b, size_t c) {
14 asm("# memcpy.inline marker");
15 return __builtin_memcpy(a, b, c);
16 }
17
18 // CHECK-LABEL: @foo(
19 // CHECK-NEXT: entry:
20 // CHECK-NEXT: [[A_ADDR_I:%.*]] = alloca i8*, align 8
21 // CHECK-NEXT: [[B_ADDR_I:%.*]] = alloca i8*, align 8
22 // CHECK-NEXT: [[C_ADDR_I:%.*]] = alloca i64, align 8
23 // CHECK-NEXT: [[A_ADDR:%.*]] = alloca i8*, align 8
24 // CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8*, align 8
25 // CHECK-NEXT: [[C_ADDR:%.*]] = alloca i64, align 8
26 // CHECK-NEXT: store i8* [[A:%.*]], i8** [[A_ADDR]], align 8
27 // CHECK-NEXT: store i8* [[B:%.*]], i8** [[B_ADDR]], align 8
28 // CHECK-NEXT: store i64 [[C:%.*]], i64* [[C_ADDR]], align 8
29 // CHECK-NEXT: [[TMP0:%.*]] = load i8*, i8** [[A_ADDR]], align 8
30 // CHECK-NEXT: [[TMP1:%.*]] = load i8*, i8** [[B_ADDR]], align 8
31 // CHECK-NEXT: [[TMP2:%.*]] = load i64, i64* [[C_ADDR]], align 8
32 // CHECK-NEXT: store i8* [[TMP0]], i8** [[A_ADDR_I]], align 8
33 // CHECK-NEXT: store i8* [[TMP1]], i8** [[B_ADDR_I]], align 8
34 // CHECK-NEXT: store i64 [[TMP2]], i64* [[C_ADDR_I]], align 8
35 // CHECK-NEXT: call void asm sideeffect "# memcpy.inline marker", "~{dirflag},~{fpsr},~{flags}"() #[[ATTR3:[0-9]+]], !srcloc !2
36 // CHECK-NEXT: [[TMP3:%.*]] = load i8*, i8** [[A_ADDR_I]], align 8
37 // CHECK-NEXT: [[TMP4:%.*]] = load i8*, i8** [[B_ADDR_I]], align 8
38 // CHECK-NEXT: [[TMP5:%.*]] = load i64, i64* [[C_ADDR_I]], align 8
39 // CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 [[TMP3]], i8* align 1 [[TMP4]], i64 [[TMP5]], i1 false)
40 // CHECK-NEXT: ret i8* [[TMP3]]
41 //
foo(void * a,const void * b,size_t c)42 void *foo(void *a, const void *b, size_t c) {
43 return memcpy(a, b, c);
44 }
45
46 // CHECK-LABEL: @bar(
47 // CHECK-NEXT: entry:
48 // CHECK-NEXT: [[A_ADDR:%.*]] = alloca i8*, align 8
49 // CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8*, align 8
50 // CHECK-NEXT: [[C_ADDR:%.*]] = alloca i64, align 8
51 // CHECK-NEXT: [[CPY:%.*]] = alloca i8* (i8*, i8*, i64)*, align 8
52 // CHECK-NEXT: store i8* [[A:%.*]], i8** [[A_ADDR]], align 8
53 // CHECK-NEXT: store i8* [[B:%.*]], i8** [[B_ADDR]], align 8
54 // CHECK-NEXT: store i64 [[C:%.*]], i64* [[C_ADDR]], align 8
55 // CHECK-NEXT: [[TMP0:%.*]] = load i64, i64* [[C_ADDR]], align 8
56 // CHECK-NEXT: [[CMP:%.*]] = icmp ugt i64 [[TMP0]], 10
57 // CHECK-NEXT: [[TMP1:%.*]] = zext i1 [[CMP]] to i64
58 // CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i8* (i8*, i8*, i64)* @memcpy, i8* (i8*, i8*, i64)* @foo
59 // CHECK-NEXT: store i8* (i8*, i8*, i64)* [[COND]], i8* (i8*, i8*, i64)** [[CPY]], align 8
60 // CHECK-NEXT: [[TMP2:%.*]] = load i8* (i8*, i8*, i64)*, i8* (i8*, i8*, i64)** [[CPY]], align 8
61 // CHECK-NEXT: [[TMP3:%.*]] = load i8*, i8** [[A_ADDR]], align 8
62 // CHECK-NEXT: [[TMP4:%.*]] = load i8*, i8** [[B_ADDR]], align 8
63 // CHECK-NEXT: [[TMP5:%.*]] = load i64, i64* [[C_ADDR]], align 8
64 // CHECK-NEXT: [[CALL:%.*]] = call i8* [[TMP2]](i8* noundef [[TMP3]], i8* noundef [[TMP4]], i64 noundef [[TMP5]])
65 // CHECK-NEXT: ret void
66 //
bar(void * a,const void * b,size_t c)67 void bar(void *a, const void *b, size_t c) {
68 void *(*cpy)(void *, const void *, size_t) = c > 10 ? memcpy : foo;
69 cpy(a, b, c);
70 }
71