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