• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2012-2016 Francisco Jerez
3 // Copyright 2012-2016 Advanced Micro Devices, Inc.
4 // Copyright 2015 Zoltan Gilian
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 // OTHER DEALINGS IN THE SOFTWARE.
23 //
24 
25 ///
26 /// \file
27 /// Codegen back-end-independent part of the construction of an executable
28 /// clover::binary, including kernel argument metadata extraction and
29 /// formatting of the pre-generated binary code in a form that can be
30 /// understood by pipe drivers.
31 ///
32 
33 #include <llvm/IR/Type.h>
34 #include <llvm/Support/Allocator.h>
35 
36 #include "llvm/codegen.hpp"
37 #include "llvm/compat.hpp"
38 #include "llvm/metadata.hpp"
39 
40 #include "CL/cl.h"
41 
42 #include "pipe/p_state.h"
43 #include "util/u_math.h"
44 
45 #include <clang/Basic/TargetInfo.h>
46 
47 using clover::binary;
48 using clover::detokenize;
49 using namespace clover::llvm;
50 
51 using ::llvm::Module;
52 using ::llvm::Function;
53 using ::llvm::Type;
54 using ::llvm::isa;
55 using ::llvm::cast;
56 using ::llvm::dyn_cast;
57 
58 namespace {
59    enum binary::argument::type
get_image_type(const std::string & type,const std::string & qual)60    get_image_type(const std::string &type,
61                   const std::string &qual) {
62       if (type == "image1d_t" || type == "image2d_t" || type == "image3d_t") {
63          if (qual == "read_only")
64             return binary::argument::image_rd;
65          else if (qual == "write_only")
66             return binary::argument::image_wr;
67       }
68 
69       unreachable("Unsupported image type");
70    }
71 
create_arg_info(const std::string & arg_name,const std::string & type_name,const std::string & type_qualifier,const uint64_t address_qualifier,const std::string & access_qualifier)72    binary::arg_info create_arg_info(const std::string &arg_name,
73                                     const std::string &type_name,
74                                     const std::string &type_qualifier,
75                                     const uint64_t address_qualifier,
76                                     const std::string &access_qualifier) {
77 
78       cl_kernel_arg_type_qualifier cl_type_qualifier =
79                                                    CL_KERNEL_ARG_TYPE_NONE;
80       if (type_qualifier.find("const") != std::string::npos)
81          cl_type_qualifier |= CL_KERNEL_ARG_TYPE_CONST;
82       if (type_qualifier.find("restrict") != std::string::npos)
83          cl_type_qualifier |=  CL_KERNEL_ARG_TYPE_RESTRICT;
84       if (type_qualifier.find("volatile") != std::string::npos)
85          cl_type_qualifier |=  CL_KERNEL_ARG_TYPE_VOLATILE;
86 
87       cl_kernel_arg_address_qualifier cl_address_qualifier =
88                                              CL_KERNEL_ARG_ADDRESS_PRIVATE;
89       if (address_qualifier == 1)
90          cl_address_qualifier = CL_KERNEL_ARG_ADDRESS_GLOBAL;
91       else if (address_qualifier == 2)
92          cl_address_qualifier =  CL_KERNEL_ARG_ADDRESS_CONSTANT;
93       else if (address_qualifier == 3)
94          cl_address_qualifier =  CL_KERNEL_ARG_ADDRESS_LOCAL;
95 
96       cl_kernel_arg_access_qualifier cl_access_qualifier =
97                                                    CL_KERNEL_ARG_ACCESS_NONE;
98       if (access_qualifier == "read_only")
99          cl_access_qualifier = CL_KERNEL_ARG_ACCESS_READ_ONLY;
100       else if (access_qualifier == "write_only")
101          cl_access_qualifier = CL_KERNEL_ARG_ACCESS_WRITE_ONLY;
102       else if (access_qualifier == "read_write")
103          cl_access_qualifier = CL_KERNEL_ARG_ACCESS_READ_WRITE;
104 
105       return binary::arg_info(arg_name, type_name, cl_type_qualifier,
106                               cl_address_qualifier, cl_access_qualifier);
107    }
108 
109    std::vector<size_t>
get_reqd_work_group_size(const Module & mod,const std::string & kernel_name)110    get_reqd_work_group_size(const Module &mod,
111                             const std::string &kernel_name) {
112       const Function &f = *mod.getFunction(kernel_name);
113       auto vector_metadata = get_uint_vector_kernel_metadata(f, "reqd_work_group_size");
114 
115       return vector_metadata.empty() ? std::vector<size_t>({0, 0, 0}) : vector_metadata;
116    }
117 
118 
119    std::string
kernel_attributes(const Module & mod,const std::string & kernel_name)120    kernel_attributes(const Module &mod, const std::string &kernel_name) {
121       std::vector<std::string> attributes;
122 
123       const Function &f = *mod.getFunction(kernel_name);
124 
125       auto vec_type_hint = get_type_kernel_metadata(f, "vec_type_hint");
126       if (!vec_type_hint.empty())
127          attributes.emplace_back("vec_type_hint(" + vec_type_hint + ")");
128 
129       auto work_group_size_hint = get_uint_vector_kernel_metadata(f, "work_group_size_hint");
130       if (!work_group_size_hint.empty()) {
131          std::string s = "work_group_size_hint(";
132          s += detokenize(work_group_size_hint, ",");
133          s += ")";
134          attributes.emplace_back(s);
135       }
136 
137       auto reqd_work_group_size = get_uint_vector_kernel_metadata(f, "reqd_work_group_size");
138       if (!reqd_work_group_size.empty()) {
139          std::string s = "reqd_work_group_size(";
140          s += detokenize(reqd_work_group_size, ",");
141          s += ")";
142          attributes.emplace_back(s);
143       }
144 
145       auto nosvm = get_str_kernel_metadata(f, "nosvm");
146       if (!nosvm.empty())
147          attributes.emplace_back("nosvm");
148 
149       return detokenize(attributes, " ");
150    }
151 
152    // Parse the type which are pointers to CL vector types with no prefix.
153    // so e.g. char/uchar, short/ushort, int/uint, long/ulong
154    // half/float/double, followed by the vector length, followed by *.
155    // uint8 is 8x32-bit integer, short4 is 4x16-bit integer etc.
156    // Since this is a pointer only path, assert the * is on the end.
157    ::llvm::Type *
ptr_arg_to_llvm_type(const Module & mod,std::string type_name)158    ptr_arg_to_llvm_type(const Module &mod, std::string type_name) {
159       int len = type_name.length();
160       assert (type_name[len-1] == '*');
161       ::llvm::Type *base_type = NULL;
162       if (type_name.find("void") != std::string::npos)
163          base_type = ::llvm::Type::getVoidTy(mod.getContext());
164       else if (type_name.find("char") != std::string::npos)
165          base_type = ::llvm::Type::getInt8Ty(mod.getContext());
166       else if (type_name.find("short") != std::string::npos)
167          base_type = ::llvm::Type::getInt16Ty(mod.getContext());
168       else if (type_name.find("int") != std::string::npos)
169          base_type = ::llvm::Type::getInt32Ty(mod.getContext());
170       else if (type_name.find("long") != std::string::npos)
171          base_type = ::llvm::Type::getInt64Ty(mod.getContext());
172       else if (type_name.find("half") != std::string::npos)
173          base_type = ::llvm::Type::getHalfTy(mod.getContext());
174       else if (type_name.find("float") != std::string::npos)
175          base_type = ::llvm::Type::getFloatTy(mod.getContext());
176       else if (type_name.find("double") != std::string::npos)
177          base_type = ::llvm::Type::getDoubleTy(mod.getContext());
178 
179       assert(base_type);
180       if (type_name.find("2") != std::string::npos)
181          base_type = ::llvm::FixedVectorType::get(base_type, 2);
182       else if (type_name.find("3") != std::string::npos)
183          base_type = ::llvm::FixedVectorType::get(base_type, 3);
184       else if (type_name.find("4") != std::string::npos)
185          base_type = ::llvm::FixedVectorType::get(base_type, 4);
186       else if (type_name.find("8") != std::string::npos)
187          base_type = ::llvm::FixedVectorType::get(base_type, 8);
188       else if (type_name.find("16") != std::string::npos)
189          base_type = ::llvm::FixedVectorType::get(base_type, 16);
190       return base_type;
191    }
192 
193    std::vector<binary::argument>
make_kernel_args(const Module & mod,const std::string & kernel_name,const clang::CompilerInstance & c)194    make_kernel_args(const Module &mod, const std::string &kernel_name,
195                     const clang::CompilerInstance &c) {
196       std::vector<binary::argument> args;
197       const Function &f = *mod.getFunction(kernel_name);
198 #if LLVM_VERSION_MAJOR >= 20
199       const ::llvm::DataLayout &dl = mod.getDataLayout();
200 #else
201       ::llvm::DataLayout dl(&mod);
202 #endif
203       const auto size_type =
204          dl.getSmallestLegalIntType(mod.getContext(), sizeof(cl_uint) * 8);
205       const unsigned size_align = compat::get_abi_type_alignment(dl, size_type);
206 
207       for (const auto &arg : f.args()) {
208          const auto arg_type = arg.getType();
209 
210          // OpenCL 1.2 specification, Ch. 6.1.5: "A built-in data
211          // type that is not a power of two bytes in size must be
212          // aligned to the next larger power of two.
213          // This rule applies to built-in types only, not structs or unions."
214          const unsigned arg_api_size = dl.getTypeAllocSize(arg_type);
215 
216          const unsigned target_size = dl.getTypeStoreSize(arg_type);
217          const unsigned target_align = compat::get_abi_type_alignment(dl, arg_type);
218 
219          const auto type_name = get_str_argument_metadata(f, arg,
220                                                           "kernel_arg_type");
221          if (type_name == "image2d_t" || type_name == "image3d_t") {
222             // Image.
223             const auto access_qual = get_str_argument_metadata(
224                f, arg, "kernel_arg_access_qual");
225             args.emplace_back(get_image_type(type_name, access_qual),
226                               target_size, target_size,
227                               target_align, binary::argument::zero_ext);
228 
229          } else if (type_name == "sampler_t") {
230             args.emplace_back(binary::argument::sampler, arg_api_size,
231                               target_size, target_align,
232                               binary::argument::zero_ext);
233 
234          } else if (type_name == "__llvm_image_size") {
235             // Image size implicit argument.
236             args.emplace_back(binary::argument::scalar, sizeof(cl_uint),
237                               dl.getTypeStoreSize(size_type),
238                               size_align,
239                               binary::argument::zero_ext,
240                               binary::argument::image_size);
241 
242          } else if (type_name == "__llvm_image_format") {
243             // Image format implicit argument.
244             args.emplace_back(binary::argument::scalar, sizeof(cl_uint),
245                               dl.getTypeStoreSize(size_type),
246                               size_align,
247                               binary::argument::zero_ext,
248                               binary::argument::image_format);
249 
250          } else {
251             // Other types.
252             const auto actual_type =
253                isa< ::llvm::PointerType>(arg_type) && arg.hasByValAttr() ?
254                ptr_arg_to_llvm_type(mod, type_name) : arg_type;
255 
256             if (actual_type->isPointerTy()) {
257                const unsigned address_space =
258                   cast< ::llvm::PointerType>(actual_type)->getAddressSpace();
259 
260                const auto &map = c.getTarget().getAddressSpaceMap();
261                const auto offset =
262                            static_cast<unsigned>(clang::LangAS::opencl_local);
263                if (address_space == map[offset]) {
264                   const auto pointee_type = ptr_arg_to_llvm_type(mod, type_name);
265 
266                   args.emplace_back(binary::argument::local, arg_api_size,
267                                     target_size,
268                                     (pointee_type->isVoidTy()) ? 8 :
269                                     compat::get_abi_type_alignment(dl, pointee_type),
270                                     binary::argument::zero_ext);
271                } else {
272                   // XXX: Correctly handle constant address space.  There is no
273                   // way for r600g to pass a handle for constant buffers back
274                   // to clover like it can for global buffers, so
275                   // creating constant arguments will break r600g.  For now,
276                   // continue treating constant buffers as global buffers
277                   // until we can come up with a way to create handles for
278                   // constant buffers.
279                   args.emplace_back(binary::argument::global, arg_api_size,
280                                     target_size, target_align,
281                                     binary::argument::zero_ext);
282                }
283 
284             } else {
285                const bool needs_sign_ext = f.getAttributes().hasParamAttr(
286                   arg.getArgNo(), ::llvm::Attribute::SExt);
287 
288                args.emplace_back(binary::argument::scalar, arg_api_size,
289                                  target_size, target_align,
290                                  (needs_sign_ext ? binary::argument::sign_ext :
291                                   binary::argument::zero_ext));
292             }
293 
294             // Add kernel argument infos if built with -cl-kernel-arg-info.
295             if (c.getCodeGenOpts().EmitOpenCLArgMetadata) {
296                args.back().info = create_arg_info(
297                   get_str_argument_metadata(f, arg, "kernel_arg_name"),
298                   type_name,
299                   get_str_argument_metadata(f, arg, "kernel_arg_type_qual"),
300                   get_uint_argument_metadata(f, arg, "kernel_arg_addr_space"),
301                   get_str_argument_metadata(f, arg, "kernel_arg_access_qual"));
302             }
303          }
304       }
305 
306       // Append implicit arguments.  XXX - The types, ordering and
307       // vector size of the implicit arguments should depend on the
308       // target according to the selected calling convention.
309       args.emplace_back(binary::argument::scalar, sizeof(cl_uint),
310                         dl.getTypeStoreSize(size_type),
311                         size_align,
312                         binary::argument::zero_ext,
313                         binary::argument::grid_dimension);
314 
315       args.emplace_back(binary::argument::scalar, sizeof(cl_uint),
316                         dl.getTypeStoreSize(size_type),
317                         size_align,
318                         binary::argument::zero_ext,
319                         binary::argument::grid_offset);
320 
321       return args;
322    }
323 
324    binary::section
make_text_section(const std::vector<char> & code)325    make_text_section(const std::vector<char> &code) {
326       const pipe_binary_program_header header { uint32_t(code.size()) };
327       binary::section text { 0, binary::section::text_executable,
328                              header.num_bytes, {} };
329 
330       text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
331                        reinterpret_cast<const char *>(&header) + sizeof(header));
332       text.data.insert(text.data.end(), code.begin(), code.end());
333 
334       return text;
335    }
336 }
337 
338 binary
build_module_common(const Module & mod,const std::vector<char> & code,const std::map<std::string,unsigned> & offsets,const clang::CompilerInstance & c)339 clover::llvm::build_module_common(const Module &mod,
340                                   const std::vector<char> &code,
341                                   const std::map<std::string,
342                                                  unsigned> &offsets,
343                                   const clang::CompilerInstance &c) {
344    binary b;
345 
346    for (const auto &llvm_name : map(std::mem_fn(&Function::getName),
347                                get_kernels(mod))) {
348       const ::std::string name(llvm_name);
349       if (offsets.count(name))
350          b.syms.emplace_back(name, kernel_attributes(mod, name),
351                              get_reqd_work_group_size(mod, name),
352                              0, offsets.at(name),
353                              make_kernel_args(mod, name, c));
354    }
355 
356    b.secs.push_back(make_text_section(code));
357    return b;
358 }
359