• 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::module, 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/codegen.hpp"
34 #include "llvm/metadata.hpp"
35 
36 #include "CL/cl.h"
37 
38 #include "pipe/p_state.h"
39 #include "util/u_math.h"
40 
41 #include <clang/Basic/TargetInfo.h>
42 
43 using namespace clover;
44 using namespace clover::llvm;
45 
46 using ::llvm::Module;
47 using ::llvm::Function;
48 using ::llvm::Type;
49 using ::llvm::isa;
50 using ::llvm::cast;
51 using ::llvm::dyn_cast;
52 
53 namespace {
54    enum module::argument::type
get_image_type(const std::string & type,const std::string & qual)55    get_image_type(const std::string &type,
56                   const std::string &qual) {
57       if (type == "image2d_t" && qual == "read_only")
58          return module::argument::image2d_rd;
59       else if (type == "image2d_t" && qual == "write_only")
60          return module::argument::image2d_wr;
61       else if (type == "image3d_t" && qual == "read_only")
62          return module::argument::image3d_rd;
63       else if (type == "image3d_t" && qual == "write_only")
64          return module::argument::image3d_wr;
65       else
66          unreachable("Unknown image type");
67    }
68 
69    std::vector<module::argument>
make_kernel_args(const Module & mod,const std::string & kernel_name,const clang::CompilerInstance & c)70    make_kernel_args(const Module &mod, const std::string &kernel_name,
71                     const clang::CompilerInstance &c) {
72       std::vector<module::argument> args;
73       const Function &f = *mod.getFunction(kernel_name);
74       ::llvm::DataLayout dl(&mod);
75       const auto size_type =
76          dl.getSmallestLegalIntType(mod.getContext(), sizeof(cl_uint) * 8);
77 
78       for (const auto &arg : f.args()) {
79          const auto arg_type = arg.getType();
80 
81          // OpenCL 1.2 specification, Ch. 6.1.5: "A built-in data
82          // type that is not a power of two bytes in size must be
83          // aligned to the next larger power of two.
84          // This rule applies to built-in types only, not structs or unions."
85          const unsigned arg_store_size = dl.getTypeStoreSize(arg_type);
86          const unsigned arg_api_size = dl.getTypeAllocSize(arg_type);
87 
88          const auto target_type = !arg_type->isIntegerTy() ? arg_type :
89             dl.getSmallestLegalIntType(mod.getContext(), arg_store_size * 8);
90          const unsigned target_size = dl.getTypeStoreSize(target_type);
91          const unsigned target_align = dl.getABITypeAlignment(target_type);
92 
93          const auto type_name = get_argument_metadata(f, arg,
94                                                       "kernel_arg_type");
95 
96          if (type_name == "image2d_t" || type_name == "image3d_t") {
97             // Image.
98             const auto access_qual = get_argument_metadata(
99                f, arg, "kernel_arg_access_qual");
100             args.emplace_back(get_image_type(type_name, access_qual),
101                               arg_store_size, target_size,
102                               target_align, module::argument::zero_ext);
103 
104          } else if (type_name == "__llvm_image_size") {
105             // Image size implicit argument.
106             args.emplace_back(module::argument::scalar, sizeof(cl_uint),
107                               dl.getTypeStoreSize(size_type),
108                               dl.getABITypeAlignment(size_type),
109                               module::argument::zero_ext,
110                               module::argument::image_size);
111 
112          } else if (type_name == "__llvm_image_format") {
113             // Image format implicit argument.
114             args.emplace_back(module::argument::scalar, sizeof(cl_uint),
115                               dl.getTypeStoreSize(size_type),
116                               dl.getABITypeAlignment(size_type),
117                               module::argument::zero_ext,
118                               module::argument::image_format);
119 
120          } else {
121             // Other types.
122             const auto actual_type =
123                isa< ::llvm::PointerType>(arg_type) && arg.hasByValAttr() ?
124                cast< ::llvm::PointerType>(arg_type)->getElementType() : arg_type;
125 
126             if (actual_type->isPointerTy()) {
127                const unsigned address_space =
128                   cast< ::llvm::PointerType>(actual_type)->getAddressSpace();
129 
130                if (address_space == compat::target_address_space(
131                                   c.getTarget(), clang::LangAS::opencl_local)) {
132                   args.emplace_back(module::argument::local, arg_api_size,
133                                     target_size, target_align,
134                                     module::argument::zero_ext);
135                } else {
136                   // XXX: Correctly handle constant address space.  There is no
137                   // way for r600g to pass a handle for constant buffers back
138                   // to clover like it can for global buffers, so
139                   // creating constant arguments will break r600g.  For now,
140                   // continue treating constant buffers as global buffers
141                   // until we can come up with a way to create handles for
142                   // constant buffers.
143                   args.emplace_back(module::argument::global, arg_api_size,
144                                     target_size, target_align,
145                                     module::argument::zero_ext);
146                }
147 
148             } else {
149                const bool needs_sign_ext = f.getAttributes().hasAttribute(
150                   arg.getArgNo() + 1, ::llvm::Attribute::SExt);
151 
152                args.emplace_back(module::argument::scalar, arg_api_size,
153                                  target_size, target_align,
154                                  (needs_sign_ext ? module::argument::sign_ext :
155                                   module::argument::zero_ext));
156             }
157          }
158       }
159 
160       // Append implicit arguments.  XXX - The types, ordering and
161       // vector size of the implicit arguments should depend on the
162       // target according to the selected calling convention.
163       args.emplace_back(module::argument::scalar, sizeof(cl_uint),
164                         dl.getTypeStoreSize(size_type),
165                         dl.getABITypeAlignment(size_type),
166                         module::argument::zero_ext,
167                         module::argument::grid_dimension);
168 
169       args.emplace_back(module::argument::scalar, sizeof(cl_uint),
170                         dl.getTypeStoreSize(size_type),
171                         dl.getABITypeAlignment(size_type),
172                         module::argument::zero_ext,
173                         module::argument::grid_offset);
174 
175       return args;
176    }
177 
178    module::section
make_text_section(const std::vector<char> & code)179    make_text_section(const std::vector<char> &code) {
180       const pipe_llvm_program_header header { uint32_t(code.size()) };
181       module::section text { 0, module::section::text_executable,
182                              header.num_bytes, {} };
183 
184       text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
185                        reinterpret_cast<const char *>(&header) + sizeof(header));
186       text.data.insert(text.data.end(), code.begin(), code.end());
187 
188       return text;
189    }
190 }
191 
192 module
193 clover::llvm::build_module_common(const Module &mod,
194                                   const std::vector<char> &code,
195                                   const std::map<std::string,
196                                                  unsigned> &offsets,
197                                   const clang::CompilerInstance &c) {
198    module m;
199 
200    for (const auto &name : map(std::mem_fn(&Function::getName),
201                                get_kernels(mod))) {
202       if (offsets.count(name))
203          m.syms.emplace_back(name, 0, offsets.at(name),
204                              make_kernel_args(mod, name, c));
205    }
206 
207    m.secs.push_back(make_text_section(code));
208    return m;
209 }
210