blob: b1ca33358e1c58dd2f0c305f473a24e60c6b657b [file] [log] [blame]
Logan Chien8b977d32012-02-21 19:14:55 +08001/*
2 * Copyright (C) 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 "compilation_unit.h"
18
Logan Chien110bcba2012-04-16 19:11:28 +080019#include "compiled_method.h"
Shih-wei Liaod7726e42012-04-20 15:23:36 -070020#include "file.h"
Elliott Hughes0f3c5532012-03-30 14:51:51 -070021#include "instruction_set.h"
Logan Chien8b977d32012-02-21 19:14:55 +080022#include "ir_builder.h"
23#include "logging.h"
Shih-wei Liaod7726e42012-04-20 15:23:36 -070024#include "os.h"
Logan Chien8b977d32012-02-21 19:14:55 +080025
TDYa127d668a062012-04-13 12:36:57 -070026#include "runtime_support_builder_arm.h"
27#include "runtime_support_builder_x86.h"
28
Logan Chien8b977d32012-02-21 19:14:55 +080029#include <llvm/ADT/OwningPtr.h>
30#include <llvm/ADT/StringSet.h>
31#include <llvm/ADT/Triple.h>
32#include <llvm/Analysis/CallGraph.h>
33#include <llvm/Analysis/DebugInfo.h>
34#include <llvm/Analysis/LoopPass.h>
35#include <llvm/Analysis/RegionPass.h>
36#include <llvm/Analysis/Verifier.h>
37#include <llvm/Assembly/PrintModulePass.h>
38#include <llvm/Bitcode/ReaderWriter.h>
39#include <llvm/CallGraphSCCPass.h>
Logan Chien110bcba2012-04-16 19:11:28 +080040#include <llvm/CodeGen/MachineFrameInfo.h>
41#include <llvm/CodeGen/MachineFunction.h>
42#include <llvm/CodeGen/MachineFunctionPass.h>
Logan Chien8b977d32012-02-21 19:14:55 +080043#include <llvm/DerivedTypes.h>
44#include <llvm/LLVMContext.h>
Logan Chien8b977d32012-02-21 19:14:55 +080045#include <llvm/Module.h>
46#include <llvm/PassManager.h>
47#include <llvm/Support/Debug.h>
48#include <llvm/Support/FormattedStream.h>
49#include <llvm/Support/ManagedStatic.h>
Shih-wei Liaod7726e42012-04-20 15:23:36 -070050#include <llvm/Support/MemoryBuffer.h>
Logan Chien8b977d32012-02-21 19:14:55 +080051#include <llvm/Support/PassNameParser.h>
52#include <llvm/Support/PluginLoader.h>
53#include <llvm/Support/PrettyStackTrace.h>
54#include <llvm/Support/Signals.h>
55#include <llvm/Support/SystemUtils.h>
56#include <llvm/Support/TargetRegistry.h>
57#include <llvm/Support/TargetSelect.h>
58#include <llvm/Support/ToolOutputFile.h>
59#include <llvm/Support/raw_ostream.h>
Shih-wei Liaod7726e42012-04-20 15:23:36 -070060#include <llvm/Support/system_error.h>
Logan Chien8b977d32012-02-21 19:14:55 +080061#include <llvm/Target/TargetData.h>
62#include <llvm/Target/TargetLibraryInfo.h>
63#include <llvm/Target/TargetMachine.h>
Shih-wei Liaof1cb9a52012-04-20 01:49:18 -070064#include <llvm/Transforms/IPO.h>
Logan Chien8b977d32012-02-21 19:14:55 +080065#include <llvm/Transforms/IPO/PassManagerBuilder.h>
66
Shih-wei Liaod7726e42012-04-20 15:23:36 -070067#include <sys/types.h>
68#include <sys/wait.h>
69#include <unistd.h>
70
Logan Chien8b977d32012-02-21 19:14:55 +080071#include <string>
72
Logan Chien110bcba2012-04-16 19:11:28 +080073namespace {
74
75class UpdateFrameSizePass : public llvm::MachineFunctionPass {
76 public:
77 static char ID;
78
79 UpdateFrameSizePass() : llvm::MachineFunctionPass(ID), cunit_(NULL) {
80 LOG(FATAL) << "Unexpected instantiation of UpdateFrameSizePass";
81 // NOTE: We have to declare this constructor for llvm::RegisterPass, but
82 // this constructor won't work because we have no information on
83 // CompilationUnit. Thus, we should place a LOG(FATAL) here.
84 }
85
86 UpdateFrameSizePass(art::compiler_llvm::CompilationUnit* cunit)
87 : llvm::MachineFunctionPass(ID), cunit_(cunit) {
88 }
89
90 virtual bool runOnMachineFunction(llvm::MachineFunction &MF) {
91 cunit_->UpdateFrameSizeInBytes(MF.getFunction(),
92 MF.getFrameInfo()->getStackSize());
93 return false;
94 }
95
96 private:
97 art::compiler_llvm::CompilationUnit* cunit_;
98};
99
100char UpdateFrameSizePass::ID = 0;
101
102llvm::RegisterPass<UpdateFrameSizePass> reg_update_frame_size_pass_(
103 "update-frame-size", "Update frame size pass", false, false);
104
105} // end anonymous namespace
106
Logan Chien8b977d32012-02-21 19:14:55 +0800107namespace art {
108namespace compiler_llvm {
109
110llvm::Module* makeLLVMModuleContents(llvm::Module* module);
111
112
Logan Chien6546ec52012-03-17 20:08:29 +0800113CompilationUnit::CompilationUnit(InstructionSet insn_set, size_t elf_idx)
114: insn_set_(insn_set), elf_idx_(elf_idx), context_(new llvm::LLVMContext()),
Logan Chien937105a2012-04-02 02:37:37 +0800115 mem_usage_(0), num_elf_funcs_(0) {
Logan Chien8b977d32012-02-21 19:14:55 +0800116
117 // Create the module and include the runtime function declaration
118 module_ = new llvm::Module("art", *context_);
119 makeLLVMModuleContents(module_);
120
121 // Create IRBuilder
122 irb_.reset(new IRBuilder(*context_, *module_));
TDYa127d668a062012-04-13 12:36:57 -0700123
124 // We always need a switch case, so just use a normal function.
125 switch(insn_set_) {
126 default:
127 runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
128 break;
129 case kArm:
130 case kThumb2:
131 runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_));
132 break;
133 case kX86:
134 runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_));
135 break;
136 }
137
138 runtime_support_->OptimizeRuntimeSupport();
139
140 irb_->SetRuntimeSupport(runtime_support_.get());
Logan Chien8b977d32012-02-21 19:14:55 +0800141}
142
143
144CompilationUnit::~CompilationUnit() {
145}
146
147
Shih-wei Liaodbd00342012-04-20 14:27:29 -0700148bool CompilationUnit::WriteBitcodeToFile(const std::string& bitcode_filename) {
Logan Chien8b977d32012-02-21 19:14:55 +0800149 std::string errmsg;
150
151 llvm::OwningPtr<llvm::tool_output_file> out_file(
Shih-wei Liaodbd00342012-04-20 14:27:29 -0700152 new llvm::tool_output_file(bitcode_filename.c_str(), errmsg,
Logan Chien8b977d32012-02-21 19:14:55 +0800153 llvm::raw_fd_ostream::F_Binary));
154
155
156 if (!errmsg.empty()) {
157 LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
158 return false;
159 }
160
161 llvm::WriteBitcodeToFile(module_, out_file->os());
162 out_file->keep();
163
164 return true;
165}
166
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700167// TODO: Move to scoped_temp_file.h
168class ScopedTempFile {
169 public:
170 ScopedTempFile(const std::string& filename_template)
171 : filename_(filename_template), file_(NULL) {
172 int fd = mkstemp(&filename_[0]);
173 CHECK_NE(-1, fd);
174 file_ = OS::FileFromFd(filename_.c_str(), fd);
Logan Chien8b977d32012-02-21 19:14:55 +0800175 }
176
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700177 ~ScopedTempFile() {
178 delete file_;
179 TEMP_FAILURE_RETRY(unlink(filename_.c_str()));
180 }
Logan Chien8b977d32012-02-21 19:14:55 +0800181
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700182 int GetFd() {
183 return file_->Fd();
184 }
Logan Chien8b977d32012-02-21 19:14:55 +0800185
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700186 const std::string &GetName() const {
187 return filename_;
188 }
Logan Chien8b977d32012-02-21 19:14:55 +0800189
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700190 bool ReadToString(std::string &buffer) {
191 off_t file_size = file_->Length();
192 if (file_size <= 0) {
193 buffer.clear();
194 return true;
195 }
Logan Chien8b977d32012-02-21 19:14:55 +0800196
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700197 buffer.reserve(file_size);
198 buffer.resize(file_size);
199 return file_->ReadFully(&buffer[0], file_size);
200 }
Logan Chien8b977d32012-02-21 19:14:55 +0800201
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700202 private:
203 std::string filename_;
204 File *file_;
205};
Logan Chien8b977d32012-02-21 19:14:55 +0800206
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700207bool CompilationUnit::Materialize() {
Shih-wei Liao795e3302012-04-21 00:20:57 -0700208 const char* android_data = getenv("ANDROID_DATA");
209 if (android_data == NULL) {
210 if (OS::DirectoryExists("/data")) {
211 android_data = "/data";
212 } else {
213 android_data = "/tmp";
214 }
215 }
216
217 std::string art_cache = GetArtCacheOrDie(android_data);
218 art_cache += "/art-llvm-XXXXXX";
Logan Chien8b977d32012-02-21 19:14:55 +0800219
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700220 // Prepare the input
Shih-wei Liao795e3302012-04-21 00:20:57 -0700221 ScopedTempFile input(art_cache);
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700222 if (input.GetFd() < 0) {
Shih-wei Liao795e3302012-04-21 00:20:57 -0700223 PLOG(ERROR) << "Failed to save the module to the file " << art_cache;
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700224 return false;
225 }
Logan Chien8b977d32012-02-21 19:14:55 +0800226
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700227 // Write the bitcode to the file
228 if (!WriteBitcodeToFile(input.GetName())) {
229 return false;
230 }
Logan Chien8b977d32012-02-21 19:14:55 +0800231
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700232 // Prepare the output
Shih-wei Liao795e3302012-04-21 00:20:57 -0700233 ScopedTempFile output(art_cache);
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700234 if (output.GetFd() < 0) {
Shih-wei Liao795e3302012-04-21 00:20:57 -0700235 PLOG(ERROR) << "Failed to prepare the output file " << art_cache;
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700236 return false;
237 }
Shih-wei Liao17765722012-04-17 16:42:19 -0700238
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700239 // Fork a process to do the compilation
240 pid_t pid = fork();
241 if (pid == 0) {
242 // change process groups, so we don't get ripped by ProcessManager
243 setpgid(0, 0);
Logan Chienb9eaeea2012-03-17 19:45:01 +0800244
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700245 // TODO: Should use exec* family instead of invoking a function.
246 // Forward our compilation request to bcc.
247 exit(static_cast<int>(!MaterializeFile(input.GetFd(), output.GetFd(),
248 insn_set_)));
249 } else {
250 if (pid < 0) {
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700251 PLOG(FATAL) << "Failed to fork a process to do the compilation";
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700252 }
253
254 // Free the resources
255 context_.reset(NULL);
256 irb_.reset(NULL);
257 module_ = NULL;
258
259 int status;
260
261 // Wait for child to finish
262 pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
263 if (got_pid != pid) {
264 PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
Logan Chienb9eaeea2012-03-17 19:45:01 +0800265 return false;
266 }
267
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700268 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
269 LOG(ERROR) << "Failed to compile the bitcode: " << WEXITSTATUS(status);
270 return false;
Shih-wei Liao17765722012-04-17 16:42:19 -0700271 }
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700272 }
Shih-wei Liao17765722012-04-17 16:42:19 -0700273
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700274 // Read the result out from the output file
275 TEMP_FAILURE_RETRY(lseek(output.GetFd(), 0, SEEK_SET));
276 if (!output.ReadToString(elf_image_)) {
277 LOG(ERROR) << "Failed to read the result file";
278 return false;
Logan Chienb9eaeea2012-03-17 19:45:01 +0800279 }
280
Logan Chiende08e842012-03-21 00:34:12 +0800281 LOG(INFO) << "Compilation Unit: " << elf_idx_ << " (done)";
Logan Chien7f767612012-03-01 18:54:49 +0800282
Logan Chien8b977d32012-02-21 19:14:55 +0800283 return true;
284}
285
286
Logan Chien110bcba2012-04-16 19:11:28 +0800287void CompilationUnit::RegisterCompiledMethod(const llvm::Function* func,
288 CompiledMethod* compiled_method) {
289 compiled_methods_map_.Put(func, compiled_method);
290}
291
292
293void CompilationUnit::UpdateFrameSizeInBytes(const llvm::Function* func,
294 size_t frame_size_in_bytes) {
295 SafeMap<const llvm::Function*, CompiledMethod*>::iterator iter =
296 compiled_methods_map_.find(func);
297
298 if (iter != compiled_methods_map_.end()) {
299 CompiledMethod* compiled_method = iter->second;
300 compiled_method->SetFrameSizeInBytes(frame_size_in_bytes);
301
302 if (frame_size_in_bytes > 1728u) {
303 LOG(WARNING) << "Huge frame size: " << frame_size_in_bytes
304 << " elf_idx=" << compiled_method->GetElfIndex()
305 << " elf_func_idx=" << compiled_method->GetElfFuncIndex();
306 }
307 }
308}
309
Shih-wei Liaod7726e42012-04-20 15:23:36 -0700310bool CompilationUnit::MaterializeFile(int input_fd, int output_fd,
311 InstructionSet insn_set) {
312 // Initialize the LLVM first
313 llvm::InitializeAllTargets();
314 llvm::InitializeAllTargetMCs();
315 llvm::InitializeAllAsmPrinters();
316 llvm::InitializeAllAsmParsers();
317
318 // Read the LLVM module from input_fd
319 llvm::OwningPtr<llvm::MemoryBuffer> memory_buffer;
320 llvm::error_code load_input_err =
321 llvm::MemoryBuffer::getOpenFile(input_fd, "<art-llvm-module>",
322 memory_buffer);
323
324 if (load_input_err) {
325 LOG(ERROR) << "Failed to load input for compiler into memory: "
326 << load_input_err.message();
327 return false;
328 }
329
330 // It's safe to use the global context now
331 std::string load_module_errmsg;
332 llvm::Module *module = ParseBitcodeFile(memory_buffer.get(),
333 llvm::getGlobalContext(),
334 &load_module_errmsg);
335 if (module == NULL) {
336 LOG(ERROR) << "Failed to load LLVM module to compiler: "
337 << load_module_errmsg;
338 return false;
339 }
340
341 // Lookup the LLVM target
342 char const* target_triple = NULL;
343 char const* target_attr = NULL;
344
345 switch (insn_set) {
346 case kThumb2:
347 target_triple = "thumb-none-linux-gnueabi";
348 target_attr = "+thumb2,+neon,+neonfp,+vfp3";
349 break;
350
351 case kArm:
352 target_triple = "armv7-none-linux-gnueabi";
353 // TODO: Fix for Xoom.
354 target_attr = "+v7,+neon,+neonfp,+vfp3";
355 break;
356
357 case kX86:
358 target_triple = "i386-pc-linux-gnu";
359 target_attr = "";
360 break;
361
362 case kMips:
363 target_triple = "mipsel-unknown-linux";
364 target_attr = "mips32r2";
365 break;
366
367 default:
368 LOG(FATAL) << "Unknown instruction set: " << insn_set;
369 }
370
371 std::string errmsg;
372 llvm::Target const* target =
373 llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
374
375 CHECK(target != NULL) << errmsg;
376
377 // Target options
378 llvm::TargetOptions target_options;
379 target_options.FloatABIType = llvm::FloatABI::Soft;
380 target_options.NoFramePointerElim = true;
381 target_options.NoFramePointerElimNonLeaf = true;
382 target_options.UseSoftFloat = false;
383 target_options.EnableFastISel = true;
384
385 // Create the llvm::TargetMachine
386 llvm::TargetMachine* target_machine =
387 target->createTargetMachine(target_triple, "", target_attr, target_options,
388 llvm::Reloc::Static, llvm::CodeModel::Small,
389 llvm::CodeGenOpt::Less);
390
391 CHECK(target_machine != NULL) << "Failed to create target machine";
392
393 // Add target data
394 llvm::TargetData const* target_data = target_machine->getTargetData();
395
396 // PassManager for code generation passes
397 llvm::PassManager pm;
398 pm.add(new llvm::TargetData(*target_data));
399
400 // FunctionPassManager for optimization pass
401 llvm::FunctionPassManager fpm(module);
402 fpm.add(new llvm::TargetData(*target_data));
403
404 // Add optimization pass
405 llvm::PassManagerBuilder pm_builder;
406 pm_builder.Inliner = llvm::createAlwaysInlinerPass();
407 pm_builder.OptLevel = 1;
408 pm_builder.DisableSimplifyLibCalls = 1;
409 pm_builder.populateModulePassManager(pm);
410 pm_builder.populateFunctionPassManager(fpm);
411
412 // Add passes to emit ELF image
413 {
414 llvm::formatted_raw_ostream formatted_os(
415 *(new llvm::raw_fd_ostream(output_fd, /* shouldClose */false)), true);
416
417 // Ask the target to add backend passes as necessary.
418 if (target_machine->addPassesToEmitFile(pm,
419 formatted_os,
420 llvm::TargetMachine::CGFT_ObjectFile,
421 true)) {
422 LOG(FATAL) << "Unable to generate ELF for this target";
423 return false;
424 }
425
426 // FIXME: Unable to run the UpdateFrameSizePass pass since it tries to
427 // update the value reside in the different address space.
428 // Add pass to update the frame_size_in_bytes_
429 //pm.add(new ::UpdateFrameSizePass(this));
430
431 // Run the per-function optimization
432 fpm.doInitialization();
433 for (llvm::Module::iterator F = module->begin(), E = module->end();
434 F != E; ++F) {
435 fpm.run(*F);
436 }
437 fpm.doFinalization();
438
439 // Run the code generation passes
440 pm.run(*module);
441 }
442
443 return true;
444}
Logan Chien110bcba2012-04-16 19:11:28 +0800445
Logan Chien8b977d32012-02-21 19:14:55 +0800446} // namespace compiler_llvm
447} // namespace art