1 | //===-- Automemcpy CodeGen Test -------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "automemcpy/CodeGen.h" |
10 | #include "automemcpy/RandomFunctionGenerator.h" |
11 | #include "gmock/gmock.h" |
12 | #include "gtest/gtest.h" |
13 | #include <optional> |
14 | |
15 | using testing::AllOf; |
16 | using testing::AnyOf; |
17 | using testing::ElementsAre; |
18 | using testing::Ge; |
19 | using testing::Gt; |
20 | using testing::Le; |
21 | using testing::Lt; |
22 | |
23 | namespace llvm { |
24 | namespace automemcpy { |
25 | namespace { |
26 | |
27 | TEST(Automemcpy, Codegen) { |
28 | static constexpr FunctionDescriptor kDescriptors[] = { |
29 | {FunctionType::MEMCPY, std::nullopt, std::nullopt, std::nullopt, std::nullopt, |
30 | Accelerator{{0, kMaxSize}}, ElementTypeClass::NATIVE}, |
31 | {FunctionType::MEMCPY, Contiguous{{0, 4}}, Overlap{{4, 256}}, |
32 | Loop{{256, kMaxSize}, 64}, std::nullopt, std::nullopt, |
33 | ElementTypeClass::NATIVE}, |
34 | {FunctionType::MEMCMP, Contiguous{{0, 2}}, Overlap{{2, 64}}, std::nullopt, |
35 | AlignedLoop{Loop{{64, kMaxSize}, 16}, 16, AlignArg::_1}, std::nullopt, |
36 | ElementTypeClass::NATIVE}, |
37 | {FunctionType::MEMSET, Contiguous{{0, 2}}, Overlap{{2, 256}}, std::nullopt, |
38 | AlignedLoop{Loop{{256, kMaxSize}, 32}, 16, AlignArg::_1}, std::nullopt, |
39 | ElementTypeClass::NATIVE}, |
40 | {FunctionType::MEMSET, Contiguous{{0, 2}}, Overlap{{2, 256}}, std::nullopt, |
41 | AlignedLoop{Loop{{256, kMaxSize}, 32}, 32, AlignArg::_1}, std::nullopt, |
42 | ElementTypeClass::NATIVE}, |
43 | {FunctionType::BZERO, Contiguous{{0, 4}}, Overlap{{4, 128}}, std::nullopt, |
44 | AlignedLoop{Loop{{128, kMaxSize}, 32}, 32, AlignArg::_1}, std::nullopt, |
45 | ElementTypeClass::NATIVE}, |
46 | }; |
47 | |
48 | std::string Output; |
49 | raw_string_ostream OutputStream(Output); |
50 | Serialize(OutputStream, kDescriptors); |
51 | |
52 | EXPECT_STREQ(OutputStream.str().c_str(), |
53 | R"(// This file is auto-generated by libc/benchmarks/automemcpy. |
54 | // Functions : 6 |
55 | |
56 | #include "LibcFunctionPrototypes.h" |
57 | #include "automemcpy/FunctionDescriptor.h" |
58 | #include "src/string/memory_utils/elements.h" |
59 | |
60 | using llvm::libc_benchmarks::BzeroConfiguration; |
61 | using llvm::libc_benchmarks::MemcmpOrBcmpConfiguration; |
62 | using llvm::libc_benchmarks::MemcpyConfiguration; |
63 | using llvm::libc_benchmarks::MemmoveConfiguration; |
64 | using llvm::libc_benchmarks::MemsetConfiguration; |
65 | |
66 | namespace LIBC_NAMESPACE { |
67 | |
68 | static void memcpy_0xE00E29EE73994E2B(char *__restrict dst, const char *__restrict src, size_t size) { |
69 | using namespace LIBC_NAMESPACE::x86; |
70 | return copy<Accelerator>(dst, src, size); |
71 | } |
72 | static void memcpy_0x7381B60C7BE75EF9(char *__restrict dst, const char *__restrict src, size_t size) { |
73 | using namespace LIBC_NAMESPACE::x86; |
74 | if(size == 0) return; |
75 | if(size == 1) return copy<_1>(dst, src); |
76 | if(size == 2) return copy<_2>(dst, src); |
77 | if(size == 3) return copy<_3>(dst, src); |
78 | if(size < 8) return copy<HeadTail<_4>>(dst, src, size); |
79 | if(size < 16) return copy<HeadTail<_8>>(dst, src, size); |
80 | if(size < 32) return copy<HeadTail<_16>>(dst, src, size); |
81 | if(size < 64) return copy<HeadTail<_32>>(dst, src, size); |
82 | if(size < 128) return copy<HeadTail<_64>>(dst, src, size); |
83 | if(size < 256) return copy<HeadTail<_128>>(dst, src, size); |
84 | return copy<Loop<_64>>(dst, src, size); |
85 | } |
86 | static int memcmp_0x348D7BA6DB0EE033(const char * lhs, const char * rhs, size_t size) { |
87 | using namespace LIBC_NAMESPACE::x86; |
88 | if(size == 0) return 0; |
89 | if(size == 1) return three_way_compare<_1>(lhs, rhs); |
90 | if(size < 4) return three_way_compare<HeadTail<_2>>(lhs, rhs, size); |
91 | if(size < 8) return three_way_compare<HeadTail<_4>>(lhs, rhs, size); |
92 | if(size < 16) return three_way_compare<HeadTail<_8>>(lhs, rhs, size); |
93 | if(size < 32) return three_way_compare<HeadTail<_16>>(lhs, rhs, size); |
94 | if(size < 64) return three_way_compare<HeadTail<_32>>(lhs, rhs, size); |
95 | return three_way_compare<Align<_16,Arg::Lhs>::Then<Loop<_16>>>(lhs, rhs, size); |
96 | } |
97 | static void memset_0x71E761699B999863(char * dst, int value, size_t size) { |
98 | using namespace LIBC_NAMESPACE::x86; |
99 | if(size == 0) return; |
100 | if(size == 1) return splat_set<_1>(dst, value); |
101 | if(size < 4) return splat_set<HeadTail<_2>>(dst, value, size); |
102 | if(size < 8) return splat_set<HeadTail<_4>>(dst, value, size); |
103 | if(size < 16) return splat_set<HeadTail<_8>>(dst, value, size); |
104 | if(size < 32) return splat_set<HeadTail<_16>>(dst, value, size); |
105 | if(size < 64) return splat_set<HeadTail<_32>>(dst, value, size); |
106 | if(size < 128) return splat_set<HeadTail<_64>>(dst, value, size); |
107 | if(size < 256) return splat_set<HeadTail<_128>>(dst, value, size); |
108 | return splat_set<Align<_16,Arg::Dst>::Then<Loop<_32>>>(dst, value, size); |
109 | } |
110 | static void memset_0x3DF0F44E2ED6A50F(char * dst, int value, size_t size) { |
111 | using namespace LIBC_NAMESPACE::x86; |
112 | if(size == 0) return; |
113 | if(size == 1) return splat_set<_1>(dst, value); |
114 | if(size < 4) return splat_set<HeadTail<_2>>(dst, value, size); |
115 | if(size < 8) return splat_set<HeadTail<_4>>(dst, value, size); |
116 | if(size < 16) return splat_set<HeadTail<_8>>(dst, value, size); |
117 | if(size < 32) return splat_set<HeadTail<_16>>(dst, value, size); |
118 | if(size < 64) return splat_set<HeadTail<_32>>(dst, value, size); |
119 | if(size < 128) return splat_set<HeadTail<_64>>(dst, value, size); |
120 | if(size < 256) return splat_set<HeadTail<_128>>(dst, value, size); |
121 | return splat_set<Align<_32,Arg::Dst>::Then<Loop<_32>>>(dst, value, size); |
122 | } |
123 | static void bzero_0x475977492C218AD4(char * dst, size_t size) { |
124 | using namespace LIBC_NAMESPACE::x86; |
125 | if(size == 0) return; |
126 | if(size == 1) return splat_set<_1>(dst, 0); |
127 | if(size == 2) return splat_set<_2>(dst, 0); |
128 | if(size == 3) return splat_set<_3>(dst, 0); |
129 | if(size < 8) return splat_set<HeadTail<_4>>(dst, 0, size); |
130 | if(size < 16) return splat_set<HeadTail<_8>>(dst, 0, size); |
131 | if(size < 32) return splat_set<HeadTail<_16>>(dst, 0, size); |
132 | if(size < 64) return splat_set<HeadTail<_32>>(dst, 0, size); |
133 | if(size < 128) return splat_set<HeadTail<_64>>(dst, 0, size); |
134 | return splat_set<Align<_32,Arg::Dst>::Then<Loop<_32>>>(dst, 0, size); |
135 | } |
136 | |
137 | } // namespace LIBC_NAMESPACE |
138 | |
139 | namespace llvm { |
140 | namespace automemcpy { |
141 | |
142 | ArrayRef<NamedFunctionDescriptor> getFunctionDescriptors() { |
143 | static constexpr NamedFunctionDescriptor kDescriptors[] = { |
144 | {"memcpy_0xE00E29EE73994E2B",{FunctionType::MEMCPY,std::nullopt,std::nullopt,std::nullopt,std::nullopt,Accelerator{{0,kMaxSize}},ElementTypeClass::NATIVE}}, |
145 | {"memcpy_0x7381B60C7BE75EF9",{FunctionType::MEMCPY,Contiguous{{0,4}},Overlap{{4,256}},Loop{{256,kMaxSize},64},std::nullopt,std::nullopt,ElementTypeClass::NATIVE}}, |
146 | {"memcmp_0x348D7BA6DB0EE033",{FunctionType::MEMCMP,Contiguous{{0,2}},Overlap{{2,64}},std::nullopt,AlignedLoop{Loop{{64,kMaxSize},16},16,AlignArg::_1},std::nullopt,ElementTypeClass::NATIVE}}, |
147 | {"memset_0x71E761699B999863",{FunctionType::MEMSET,Contiguous{{0,2}},Overlap{{2,256}},std::nullopt,AlignedLoop{Loop{{256,kMaxSize},32},16,AlignArg::_1},std::nullopt,ElementTypeClass::NATIVE}}, |
148 | {"memset_0x3DF0F44E2ED6A50F",{FunctionType::MEMSET,Contiguous{{0,2}},Overlap{{2,256}},std::nullopt,AlignedLoop{Loop{{256,kMaxSize},32},32,AlignArg::_1},std::nullopt,ElementTypeClass::NATIVE}}, |
149 | {"bzero_0x475977492C218AD4",{FunctionType::BZERO,Contiguous{{0,4}},Overlap{{4,128}},std::nullopt,AlignedLoop{Loop{{128,kMaxSize},32},32,AlignArg::_1},std::nullopt,ElementTypeClass::NATIVE}}, |
150 | }; |
151 | return ArrayRef(kDescriptors); |
152 | } |
153 | |
154 | } // namespace automemcpy |
155 | } // namespace llvm |
156 | |
157 | |
158 | using MemcpyStub = void (*)(char *__restrict, const char *__restrict, size_t); |
159 | template <MemcpyStub Foo> |
160 | void *Wrap(void *__restrict dst, const void *__restrict src, size_t size) { |
161 | Foo(reinterpret_cast<char *__restrict>(dst), |
162 | reinterpret_cast<const char *__restrict>(src), size); |
163 | return dst; |
164 | } |
165 | llvm::ArrayRef<MemcpyConfiguration> getMemcpyConfigurations() { |
166 | using namespace LIBC_NAMESPACE; |
167 | static constexpr MemcpyConfiguration kConfigurations[] = { |
168 | {Wrap<memcpy_0xE00E29EE73994E2B>, "memcpy_0xE00E29EE73994E2B"}, |
169 | {Wrap<memcpy_0x7381B60C7BE75EF9>, "memcpy_0x7381B60C7BE75EF9"}, |
170 | }; |
171 | return llvm::ArrayRef(kConfigurations); |
172 | } |
173 | |
174 | using MemcmpStub = int (*)(const char *, const char *, size_t); |
175 | template <MemcmpStub Foo> |
176 | int Wrap(const void *lhs, const void *rhs, size_t size) { |
177 | return Foo(reinterpret_cast<const char *>(lhs), |
178 | reinterpret_cast<const char *>(rhs), size); |
179 | } |
180 | llvm::ArrayRef<MemcmpOrBcmpConfiguration> getMemcmpConfigurations() { |
181 | using namespace LIBC_NAMESPACE; |
182 | static constexpr MemcmpOrBcmpConfiguration kConfigurations[] = { |
183 | {Wrap<memcmp_0x348D7BA6DB0EE033>, "memcmp_0x348D7BA6DB0EE033"}, |
184 | }; |
185 | return llvm::ArrayRef(kConfigurations); |
186 | } |
187 | llvm::ArrayRef<MemcmpOrBcmpConfiguration> getBcmpConfigurations() { |
188 | return {}; |
189 | } |
190 | |
191 | using MemsetStub = void (*)(char *, int, size_t); |
192 | template <MemsetStub Foo> void *Wrap(void *dst, int value, size_t size) { |
193 | Foo(reinterpret_cast<char *>(dst), value, size); |
194 | return dst; |
195 | } |
196 | llvm::ArrayRef<MemsetConfiguration> getMemsetConfigurations() { |
197 | using namespace LIBC_NAMESPACE; |
198 | static constexpr MemsetConfiguration kConfigurations[] = { |
199 | {Wrap<memset_0x71E761699B999863>, "memset_0x71E761699B999863"}, |
200 | {Wrap<memset_0x3DF0F44E2ED6A50F>, "memset_0x3DF0F44E2ED6A50F"}, |
201 | }; |
202 | return llvm::ArrayRef(kConfigurations); |
203 | } |
204 | |
205 | using BzeroStub = void (*)(char *, size_t); |
206 | template <BzeroStub Foo> void Wrap(void *dst, size_t size) { |
207 | Foo(reinterpret_cast<char *>(dst), size); |
208 | } |
209 | llvm::ArrayRef<BzeroConfiguration> getBzeroConfigurations() { |
210 | using namespace LIBC_NAMESPACE; |
211 | static constexpr BzeroConfiguration kConfigurations[] = { |
212 | {Wrap<bzero_0x475977492C218AD4>, "bzero_0x475977492C218AD4"}, |
213 | }; |
214 | return llvm::ArrayRef(kConfigurations); |
215 | } |
216 | |
217 | llvm::ArrayRef<MemmoveConfiguration> getMemmoveConfigurations() { |
218 | return {}; |
219 | } |
220 | // Functions : 6 |
221 | )" ); |
222 | } |
223 | } // namespace |
224 | } // namespace automemcpy |
225 | } // namespace llvm |
226 | |