invocation.cpp revision ca8fa0230896727af81ee54197f6e2233d49481d
1//
2// Copyright 2012 Francisco Jerez
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17// THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20// SOFTWARE.
21//
22
23#include "core/compiler.hpp"
24
25#include <clang/Frontend/CompilerInstance.h>
26#include <clang/Frontend/TextDiagnosticPrinter.h>
27#include <clang/CodeGen/CodeGenAction.h>
28#include <llvm/Bitcode/BitstreamWriter.h>
29#include <llvm/Bitcode/ReaderWriter.h>
30#include <llvm/DerivedTypes.h>
31#include <llvm/Linker.h>
32#include <llvm/LLVMContext.h>
33#include <llvm/Module.h>
34#include <llvm/PassManager.h>
35#include <llvm/Support/TargetSelect.h>
36#include <llvm/Support/MemoryBuffer.h>
37#include <llvm/Support/PathV1.h>
38#include <llvm/Target/TargetData.h>
39#include <llvm/Transforms/IPO.h>
40#include <llvm/Transforms/IPO/PassManagerBuilder.h>
41
42#include "pipe/p_state.h"
43#include "util/u_memory.h"
44
45#include <iostream>
46#include <iomanip>
47#include <fstream>
48#include <cstdio>
49
50using namespace clover;
51
52namespace {
53#if 0
54   void
55   build_binary(const std::string &source, const std::string &target,
56                const std::string &name) {
57      clang::CompilerInstance c;
58      clang::EmitObjAction act(&llvm::getGlobalContext());
59      std::string log;
60      llvm::raw_string_ostream s_log(log);
61
62      LLVMInitializeTGSITarget();
63      LLVMInitializeTGSITargetInfo();
64      LLVMInitializeTGSITargetMC();
65      LLVMInitializeTGSIAsmPrinter();
66
67      c.getFrontendOpts().Inputs.push_back(
68         std::make_pair(clang::IK_OpenCL, name));
69      c.getHeaderSearchOpts().UseBuiltinIncludes = false;
70      c.getHeaderSearchOpts().UseStandardIncludes = false;
71      c.getLangOpts().NoBuiltin = true;
72      c.getTargetOpts().Triple = target;
73      c.getInvocation().setLangDefaults(clang::IK_OpenCL);
74      c.createDiagnostics(0, NULL, new clang::TextDiagnosticPrinter(
75                             s_log, c.getDiagnosticOpts()));
76
77      c.getPreprocessorOpts().addRemappedFile(
78         name, llvm::MemoryBuffer::getMemBuffer(source));
79
80      if (!c.ExecuteAction(act))
81         throw build_error(log);
82   }
83
84   module
85   load_binary(const char *name) {
86      std::ifstream fs((name));
87      std::vector<unsigned char> str((std::istreambuf_iterator<char>(fs)),
88                                     (std::istreambuf_iterator<char>()));
89      compat::istream cs(str);
90      return module::deserialize(cs);
91   }
92#endif
93
94   llvm::Module *
95   compile(const std::string &source, const std::string &name,
96           const std::string &triple) {
97
98      clang::CompilerInstance c;
99      clang::EmitLLVMOnlyAction act(&llvm::getGlobalContext());
100      std::string log;
101      llvm::raw_string_ostream s_log(log);
102
103      c.getFrontendOpts().Inputs.push_back(
104            clang::FrontendInputFile(name, clang::IK_OpenCL));
105      c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
106      c.getHeaderSearchOpts().UseBuiltinIncludes = true;
107      c.getHeaderSearchOpts().UseStandardSystemIncludes = true;
108      c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR;
109
110      // Add libclc generic search path
111      c.getHeaderSearchOpts().AddPath(LIBCLC_PATH "/generic/include/",
112                                      clang::frontend::Angled,
113                                      false, false, false);
114
115      // Add libclc include
116      c.getPreprocessorOpts().Includes.push_back("clc/clc.h");
117
118      // clc.h requires that this macro be defined:
119      c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers");
120
121      c.getLangOpts().NoBuiltin = true;
122      c.getTargetOpts().Triple = triple;
123      c.getInvocation().setLangDefaults(clang::IK_OpenCL);
124      c.createDiagnostics(0, NULL, new clang::TextDiagnosticPrinter(
125                          s_log, c.getDiagnosticOpts()));
126
127      c.getPreprocessorOpts().addRemappedFile(name,
128                                      llvm::MemoryBuffer::getMemBuffer(source));
129
130      // Compile the code
131      if (!c.ExecuteAction(act))
132         throw build_error(log);
133
134      return act.takeModule();
135   }
136
137   void
138   find_kernels(llvm::Module *mod, std::vector<llvm::Function *> &kernels) {
139      const llvm::NamedMDNode *kernel_node =
140                                 mod->getNamedMetadata("opencl.kernels");
141      for (unsigned i = 0; i < kernel_node->getNumOperands(); ++i) {
142         kernels.push_back(llvm::dyn_cast<llvm::Function>(
143                                    kernel_node->getOperand(i)->getOperand(0)));
144      }
145   }
146
147   void
148   link(llvm::Module *mod, const std::string &triple,
149        const std::vector<llvm::Function *> &kernels) {
150
151      llvm::PassManager PM;
152      llvm::PassManagerBuilder Builder;
153      bool isNative;
154      llvm::Linker linker("clover", mod);
155
156      // Link the kernel with libclc
157      linker.LinkInFile(llvm::sys::Path(LIBCLC_PATH + triple + "/lib/builtins.bc"), isNative);
158      mod = linker.releaseModule();
159
160      // Add a function internalizer pass.
161      //
162      // By default, the function internalizer pass will look for a function
163      // called "main" and then mark all other functions as internal.  Marking
164      // functions as internal enables the optimizer to perform optimizations
165      // like function inlining and global dead-code elimination.
166      //
167      // When there is no "main" function in a module, the internalize pass will
168      // treat the module like a library, and it won't internalize any functions.
169      // Since there is no "main" function in our kernels, we need to tell
170      // the internalizer pass that this module is not a library by passing a
171      // list of kernel functions to the internalizer.  The internalizer will
172      // treat the functions in the list as "main" functions and internalize
173      // all of the other functions.
174      std::vector<const char*> export_list;
175      for (std::vector<llvm::Function *>::const_iterator I = kernels.begin(),
176                                                         E = kernels.end();
177                                                         I != E; ++I) {
178         llvm::Function *kernel = *I;
179         export_list.push_back(kernel->getName().data());
180      }
181      PM.add(llvm::createInternalizePass(export_list));
182
183      // Run link time optimizations
184      Builder.OptLevel = 2;
185      Builder.populateLTOPassManager(PM, false, true);
186      PM.run(*mod);
187   }
188
189   module
190   build_module_llvm(llvm::Module *mod,
191                     const std::vector<llvm::Function *> &kernels) {
192
193      module m;
194      struct pipe_llvm_program_header header;
195
196      llvm::SmallVector<char, 1024> llvm_bitcode;
197      llvm::raw_svector_ostream bitcode_ostream(llvm_bitcode);
198      llvm::BitstreamWriter writer(llvm_bitcode);
199      llvm::WriteBitcodeToFile(mod, bitcode_ostream);
200      bitcode_ostream.flush();
201
202      llvm::Function *kernel_func;
203      std::string kernel_name;
204      compat::vector<module::argument> args;
205
206      // XXX: Support more than one kernel
207      assert(kernels.size() == 1);
208
209      kernel_func = kernels[0];
210      kernel_name = kernel_func->getName();
211
212      for (llvm::Function::arg_iterator I = kernel_func->arg_begin(),
213                                   E = kernel_func->arg_end(); I != E; ++I) {
214         llvm::Argument &arg = *I;
215         llvm::Type *arg_type = arg.getType();
216         llvm::TargetData TD(kernel_func->getParent());
217         unsigned arg_size = TD.getTypeStoreSize(arg_type);
218
219         if (llvm::isa<llvm::PointerType>(arg_type) && arg.hasByValAttr()) {
220            arg_type =
221               llvm::dyn_cast<llvm::PointerType>(arg_type)->getElementType();
222         }
223
224         if (arg_type->isPointerTy()) {
225            // XXX: Figure out LLVM->OpenCL address space mappings for each
226            // target.  I think we need to ask clang what these are.  For now,
227            // pretend everything is in the global address space.
228            unsigned address_space = llvm::cast<llvm::PointerType>(arg_type)->getAddressSpace();
229            switch (address_space) {
230               default:
231                  args.push_back(module::argument(module::argument::global, arg_size));
232                  break;
233            }
234         } else {
235            args.push_back(module::argument(module::argument::scalar, arg_size));
236         }
237      }
238
239      header.num_bytes = llvm_bitcode.size();
240      std::string data;
241      data.insert(0, (char*)(&header), sizeof(header));
242      data.insert(data.end(), llvm_bitcode.begin(),
243                                  llvm_bitcode.end());
244      m.syms.push_back(module::symbol(kernel_name, 0, 0, args ));
245      m.secs.push_back(module::section(0, module::section::text,
246                                       header.num_bytes, data));
247
248      return m;
249   }
250} // End anonymous namespace
251
252module
253clover::compile_program_llvm(const compat::string &source,
254                             enum pipe_shader_ir ir,
255                             const compat::string &triple) {
256
257   std::vector<llvm::Function *> kernels;
258
259   llvm::Module *mod = compile(source, "cl_input", triple);
260
261   find_kernels(mod, kernels);
262
263   link(mod, triple, kernels);
264
265   // Build the clover::module
266   switch (ir) {
267      case PIPE_SHADER_IR_TGSI:
268         //XXX: Handle TGSI
269         assert(0);
270         return module();
271      default:
272         return build_module_llvm(mod, kernels);
273   }
274}
275