1/*
2 * Copyright 2010-2012, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "bcc/Compiler.h"
18
19#include <llvm/Analysis/Passes.h>
20#include <llvm/CodeGen/RegAllocRegistry.h>
21#include <llvm/IR/Module.h>
22#include <llvm/PassManager.h>
23#include <llvm/Support/TargetRegistry.h>
24#include <llvm/Support/raw_ostream.h>
25#include <llvm/IR/DataLayout.h>
26#include <llvm/Target/TargetMachine.h>
27#include <llvm/Transforms/IPO.h>
28#include <llvm/Transforms/IPO/PassManagerBuilder.h>
29#include <llvm/Transforms/Scalar.h>
30
31#include "bcc/Script.h"
32#include "bcc/Source.h"
33#include "bcc/Support/CompilerConfig.h"
34#include "bcc/Support/Log.h"
35#include "bcc/Support/OutputFile.h"
36
37#include <string>
38
39using namespace bcc;
40
41const char *Compiler::GetErrorString(enum ErrorCode pErrCode) {
42  switch (pErrCode) {
43  case kSuccess:
44    return "Successfully compiled.";
45  case kInvalidConfigNoTarget:
46    return "Invalid compiler config supplied (getTarget() returns NULL.) "
47           "(missing call to CompilerConfig::initialize()?)";
48  case kErrCreateTargetMachine:
49    return "Failed to create llvm::TargetMachine.";
50  case kErrSwitchTargetMachine:
51    return  "Failed to switch llvm::TargetMachine.";
52  case kErrNoTargetMachine:
53    return "Failed to compile the script since there's no available "
54           "TargetMachine. (missing call to Compiler::config()?)";
55  case kErrDataLayoutNoMemory:
56    return "Out of memory when create DataLayout during compilation.";
57  case kErrMaterialization:
58    return "Failed to materialize the module.";
59  case kErrInvalidOutputFileState:
60    return "Supplied output file was invalid (in the error state.)";
61  case kErrPrepareOutput:
62    return "Failed to prepare file for output.";
63  case kPrepareCodeGenPass:
64    return "Failed to construct pass list for code-generation.";
65  case kErrHookBeforeAddLTOPasses:
66    return "Error occurred during beforeAddLTOPasses() in subclass.";
67  case kErrHookAfterAddLTOPasses:
68    return "Error occurred during afterAddLTOPasses() in subclass.";
69  case kErrHookAfterExecuteLTOPasses:
70    return "Error occurred during afterExecuteLTOPasses() in subclass.";
71  case kErrHookBeforeAddCodeGenPasses:
72    return "Error occurred during beforeAddCodeGenPasses() in subclass.";
73  case kErrHookAfterAddCodeGenPasses:
74    return "Error occurred during afterAddCodeGenPasses() in subclass.";
75  case kErrHookBeforeExecuteCodeGenPasses:
76    return "Error occurred during beforeExecuteCodeGenPasses() in subclass.";
77  case kErrHookAfterExecuteCodeGenPasses:
78    return "Error occurred during afterExecuteCodeGenPasses() in subclass.";
79  case kErrInvalidSource:
80    return "Error loading input bitcode";
81  }
82
83  // This assert should never be reached as the compiler verifies that the
84  // above switch coveres all enum values.
85  assert(false && "Unknown error code encountered");
86  return  "";
87}
88
89//===----------------------------------------------------------------------===//
90// Instance Methods
91//===----------------------------------------------------------------------===//
92Compiler::Compiler() : mTarget(NULL), mEnableLTO(true) {
93  return;
94}
95
96Compiler::Compiler(const CompilerConfig &pConfig) : mTarget(NULL),
97                                                    mEnableLTO(true) {
98  const std::string &triple = pConfig.getTriple();
99
100  enum ErrorCode err = config(pConfig);
101  if (err != kSuccess) {
102    ALOGE("%s (%s, features: %s)", GetErrorString(err),
103          triple.c_str(), pConfig.getFeatureString().c_str());
104    return;
105  }
106
107  return;
108}
109
110enum Compiler::ErrorCode Compiler::config(const CompilerConfig &pConfig) {
111  if (pConfig.getTarget() == NULL) {
112    return kInvalidConfigNoTarget;
113  }
114
115  llvm::TargetMachine *new_target =
116      (pConfig.getTarget())->createTargetMachine(pConfig.getTriple(),
117                                                 pConfig.getCPU(),
118                                                 pConfig.getFeatureString(),
119                                                 pConfig.getTargetOptions(),
120                                                 pConfig.getRelocationModel(),
121                                                 pConfig.getCodeModel(),
122                                                 pConfig.getOptimizationLevel());
123
124  if (new_target == NULL) {
125    return ((mTarget != NULL) ? kErrSwitchTargetMachine :
126                                kErrCreateTargetMachine);
127  }
128
129  // Replace the old TargetMachine.
130  delete mTarget;
131  mTarget = new_target;
132
133  // Adjust register allocation policy according to the optimization level.
134  //  createFastRegisterAllocator: fast but bad quality
135  //  createLinearScanRegisterAllocator: not so fast but good quality
136  if ((pConfig.getOptimizationLevel() == llvm::CodeGenOpt::None)) {
137    llvm::RegisterRegAlloc::setDefault(llvm::createFastRegisterAllocator);
138  } else {
139    llvm::RegisterRegAlloc::setDefault(llvm::createGreedyRegisterAllocator);
140  }
141
142  return kSuccess;
143}
144
145Compiler::~Compiler() {
146  delete mTarget;
147}
148
149enum Compiler::ErrorCode Compiler::runLTO(Script &pScript) {
150  llvm::DataLayoutPass *data_layout_pass = NULL;
151
152  // Pass manager for link-time optimization
153  llvm::PassManager lto_passes;
154
155  // Prepare DataLayout target data from Module
156  data_layout_pass = new (std::nothrow) llvm::DataLayoutPass(*mTarget->getDataLayout());
157  if (data_layout_pass == NULL) {
158    return kErrDataLayoutNoMemory;
159  }
160
161  // Add DataLayout to the pass manager.
162  lto_passes.add(data_layout_pass);
163
164  // Invoke "beforeAddLTOPasses" before adding the first pass.
165  if (!beforeAddLTOPasses(pScript, lto_passes)) {
166    return kErrHookBeforeAddLTOPasses;
167  }
168
169  if (mTarget->getOptLevel() == llvm::CodeGenOpt::None) {
170    lto_passes.add(llvm::createGlobalOptimizerPass());
171    lto_passes.add(llvm::createConstantMergePass());
172  } else {
173    // FIXME: Figure out which passes should be executed.
174    llvm::PassManagerBuilder Builder;
175    Builder.populateLTOPassManager(lto_passes, /*Internalize*/false,
176                                   /*RunInliner*/true);
177  }
178
179  // Invoke "afterAddLTOPasses" after pass manager finished its
180  // construction.
181  if (!afterAddLTOPasses(pScript, lto_passes)) {
182    return kErrHookAfterAddLTOPasses;
183  }
184
185  lto_passes.run(pScript.getSource().getModule());
186
187  // Invoke "afterExecuteLTOPasses" before returning.
188  if (!afterExecuteLTOPasses(pScript)) {
189    return kErrHookAfterExecuteLTOPasses;
190  }
191
192  return kSuccess;
193}
194
195enum Compiler::ErrorCode Compiler::runCodeGen(Script &pScript,
196                                              llvm::raw_ostream &pResult) {
197  llvm::DataLayoutPass *data_layout_pass;
198  llvm::MCContext *mc_context = NULL;
199
200  // Create pass manager for MC code generation.
201  llvm::PassManager codegen_passes;
202
203  // Prepare DataLayout target data from Module
204  data_layout_pass = new (std::nothrow) llvm::DataLayoutPass(*mTarget->getDataLayout());
205  if (data_layout_pass == NULL) {
206    return kErrDataLayoutNoMemory;
207  }
208
209  // Add DataLayout to the pass manager.
210  codegen_passes.add(data_layout_pass);
211
212  // Invokde "beforeAddCodeGenPasses" before adding the first pass.
213  if (!beforeAddCodeGenPasses(pScript, codegen_passes)) {
214    return kErrHookBeforeAddCodeGenPasses;
215  }
216
217  // Add passes to the pass manager to emit machine code through MC layer.
218  if (mTarget->addPassesToEmitMC(codegen_passes, mc_context, pResult,
219                                 /* DisableVerify */false)) {
220    return kPrepareCodeGenPass;
221  }
222
223  // Invokde "afterAddCodeGenPasses" after pass manager finished its
224  // construction.
225  if (!afterAddCodeGenPasses(pScript, codegen_passes)) {
226    return kErrHookAfterAddCodeGenPasses;
227  }
228
229  // Invokde "beforeExecuteCodeGenPasses" before executing the passes.
230  if (!beforeExecuteCodeGenPasses(pScript, codegen_passes)) {
231    return kErrHookBeforeExecuteCodeGenPasses;
232  }
233
234  // Execute the pass.
235  codegen_passes.run(pScript.getSource().getModule());
236
237  // Invokde "afterExecuteCodeGenPasses" before returning.
238  if (!afterExecuteCodeGenPasses(pScript)) {
239    return kErrHookAfterExecuteCodeGenPasses;
240  }
241
242  return kSuccess;
243}
244
245enum Compiler::ErrorCode Compiler::compile(Script &pScript,
246                                           llvm::raw_ostream &pResult,
247                                           llvm::raw_ostream *IRStream) {
248  llvm::Module &module = pScript.getSource().getModule();
249  enum ErrorCode err;
250
251  if (mTarget == NULL) {
252    return kErrNoTargetMachine;
253  }
254
255  const std::string &triple = module.getTargetTriple();
256  const llvm::DataLayout *dl = getTargetMachine().getDataLayout();
257  unsigned int pointerSize = dl->getPointerSizeInBits();
258  if (triple == "armv7-none-linux-gnueabi") {
259    if (pointerSize != 32) {
260      return kErrInvalidSource;
261    }
262  } else if (triple == "aarch64-none-linux-gnueabi") {
263    if (pointerSize != 64) {
264      return kErrInvalidSource;
265    }
266  } else {
267    return kErrInvalidSource;
268  }
269
270  // Materialize the bitcode module.
271  if (module.getMaterializer() != NULL) {
272    // A module with non-null materializer means that it is a lazy-load module.
273    // Materialize it now via invoking MaterializeAllPermanently(). This
274    // function returns false when the materialization is successful.
275    std::error_code ec = module.materializeAllPermanently();
276    if (ec) {
277      ALOGE("Failed to materialize the module `%s'! (%s)",
278            module.getModuleIdentifier().c_str(), ec.message().c_str());
279      return kErrMaterialization;
280    }
281  }
282
283  if (mEnableLTO && ((err = runLTO(pScript)) != kSuccess)) {
284    return err;
285  }
286
287  if (IRStream)
288    *IRStream << module;
289
290  if ((err = runCodeGen(pScript, pResult)) != kSuccess) {
291    return err;
292  }
293
294  return kSuccess;
295}
296
297enum Compiler::ErrorCode Compiler::compile(Script &pScript,
298                                           OutputFile &pResult,
299                                           llvm::raw_ostream *IRStream) {
300  // Check the state of the specified output file.
301  if (pResult.hasError()) {
302    return kErrInvalidOutputFileState;
303  }
304
305  // Open the output file decorated in llvm::raw_ostream.
306  llvm::raw_ostream *out = pResult.dup();
307  if (out == NULL) {
308    return kErrPrepareOutput;
309  }
310
311  // Delegate the request.
312  enum Compiler::ErrorCode err = compile(pScript, *out, IRStream);
313
314  // Close the output before return.
315  delete out;
316
317  return err;
318}
319