Implement runtime support inlining.

Change-Id: I8608b246a4dfde9959b5b86872f65dfa61646c84
diff --git a/src/compiler_llvm/runtime_support_builder.cc b/src/compiler_llvm/runtime_support_builder.cc
new file mode 100644
index 0000000..4183df6
--- /dev/null
+++ b/src/compiler_llvm/runtime_support_builder.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "runtime_support_builder.h"
+
+#include "ir_builder.h"
+#include "shadow_frame.h"
+#include "thread.h"
+
+#include <llvm/DerivedTypes.h>
+#include <llvm/Function.h>
+#include <llvm/Module.h>
+#include <llvm/Type.h>
+
+using namespace llvm;
+
+namespace art {
+namespace compiler_llvm {
+
+using namespace runtime_support;
+
+
+RuntimeSupportBuilder::RuntimeSupportBuilder(llvm::LLVMContext& context,
+                                             llvm::Module& module,
+                                             IRBuilder& irb)
+    : context_(context), module_(module), irb_(irb)
+{
+#define GET_RUNTIME_SUPPORT_FUNC_DECL(ID, NAME) \
+  do { \
+    llvm::Function* fn = module_.getFunction(#NAME); \
+    DCHECK_NE(fn, (void*)NULL) << "Function not found: " << #NAME; \
+    runtime_support_func_decls_[ID] = fn; \
+  } while (0);
+
+#include "runtime_support_func_list.h"
+  RUNTIME_SUPPORT_FUNC_LIST(GET_RUNTIME_SUPPORT_FUNC_DECL)
+#undef RUNTIME_SUPPORT_FUNC_LIST
+#undef GET_RUNTIME_SUPPORT_FUNC_DECL
+}
+
+void RuntimeSupportBuilder::MakeFunctionInline(llvm::Function* func) {
+  func->setLinkage(GlobalValue::LinkOnceODRLinkage);
+
+  SmallVector<AttributeWithIndex, 4> Attrs;
+  AttributeWithIndex PAWI;
+  PAWI.Index = ~0U;
+  PAWI.Attrs = Attribute::None | Attribute::NoUnwind | Attribute::AlwaysInline;
+  Attrs.push_back(PAWI);
+  AttrListPtr func_PAL = AttrListPtr::get(Attrs.begin(), Attrs.end());
+
+  func->setAttributes(func_PAL);
+}
+
+void RuntimeSupportBuilder::OverrideRuntimeSupportFunction(RuntimeId id, llvm::Function* function) {
+  // TODO: Check function prototype.
+  if (id >= 0 && id < MAX_ID) {
+    runtime_support_func_decls_[id] = function;
+    target_runtime_support_func_[id] = true;
+  } else {
+    LOG(ERROR) << "Unknown runtime function id: " << id;
+  }
+}
+
+void RuntimeSupportBuilder::OptimizeRuntimeSupport() {
+  TargetOptimizeRuntimeSupport();
+
+  if (!target_runtime_support_func_[PushShadowFrame]) {
+    Function* func = GetRuntimeSupportFunction(PushShadowFrame);
+    MakeFunctionInline(func);
+    BasicBlock* basic_block = BasicBlock::Create(context_, "entry", func);
+    irb_.SetInsertPoint(basic_block);
+
+    Function* get_thread = GetRuntimeSupportFunction(GetCurrentThread);
+    Value* thread = irb_.CreateCall(get_thread);
+    Value* new_shadow_frame = func->arg_begin();
+    Value* old_shadow_frame = irb_.LoadFromObjectOffset(thread,
+                                                        Thread::TopShadowFrameOffset().Int32Value(),
+                                                        irb_.getJObjectTy());
+    irb_.StoreToObjectOffset(new_shadow_frame,
+                             ShadowFrame::LinkOffset(),
+                             old_shadow_frame);
+    irb_.StoreToObjectOffset(thread,
+                             Thread::TopShadowFrameOffset().Int32Value(),
+                             new_shadow_frame);
+    irb_.CreateRetVoid();
+  }
+
+  if (!target_runtime_support_func_[PopShadowFrame]) {
+    Function* func = GetRuntimeSupportFunction(PopShadowFrame);
+    MakeFunctionInline(func);
+    BasicBlock* basic_block = BasicBlock::Create(context_, "entry", func);
+    irb_.SetInsertPoint(basic_block);
+
+    Function* get_thread = GetRuntimeSupportFunction(GetCurrentThread);
+    Value* thread = irb_.CreateCall(get_thread);
+    Value* new_shadow_frame = irb_.LoadFromObjectOffset(thread,
+                                                        Thread::TopShadowFrameOffset().Int32Value(),
+                                                        irb_.getJObjectTy());
+    Value* old_shadow_frame = irb_.LoadFromObjectOffset(new_shadow_frame,
+                                                        ShadowFrame::LinkOffset(),
+                                                        irb_.getJObjectTy());
+    irb_.StoreToObjectOffset(thread,
+                             Thread::TopShadowFrameOffset().Int32Value(),
+                             old_shadow_frame);
+    irb_.CreateRetVoid();
+  }
+
+  if (!target_runtime_support_func_[IsExceptionPending]) {
+    Function* func = GetRuntimeSupportFunction(IsExceptionPending);
+    MakeFunctionInline(func);
+    BasicBlock* basic_block = BasicBlock::Create(context_, "entry", func);
+    irb_.SetInsertPoint(basic_block);
+
+    Function* get_thread = GetRuntimeSupportFunction(GetCurrentThread);
+    Value* thread = irb_.CreateCall(get_thread);
+    Value* exception = irb_.LoadFromObjectOffset(thread,
+                                                 Thread::ExceptionOffset().Int32Value(),
+                                                 irb_.getJObjectTy());
+    Value* is_exception_not_null = irb_.CreateICmpNE(exception, irb_.getJNull());
+    irb_.CreateRet(is_exception_not_null);
+  }
+
+  if (!target_runtime_support_func_[TestSuspend]) {
+    Function* slow_func = GetRuntimeSupportFunction(TestSuspend);
+
+    Function* func = Function::Create(slow_func->getFunctionType(),
+                                      GlobalValue::LinkOnceODRLinkage,
+                                      "test_suspend_fast",
+                                      &module_);
+    MakeFunctionInline(func);
+    BasicBlock* basic_block = BasicBlock::Create(context_, "entry", func);
+    irb_.SetInsertPoint(basic_block);
+
+    Function* get_thread = GetRuntimeSupportFunction(GetCurrentThread);
+    Value* thread = irb_.CreateCall(get_thread);
+    Value* suspend_count = irb_.LoadFromObjectOffset(thread,
+                                                     Thread::SuspendCountOffset().Int32Value(),
+                                                     irb_.getJIntTy());
+    Value* is_suspend = irb_.CreateICmpNE(suspend_count, irb_.getJInt(0));
+
+    BasicBlock* basic_block_suspend = BasicBlock::Create(context_, "suspend", func);
+    BasicBlock* basic_block_else = BasicBlock::Create(context_, "else", func);
+    irb_.CreateCondBr(is_suspend, basic_block_suspend, basic_block_else);
+    irb_.SetInsertPoint(basic_block_suspend);
+    irb_.CreateCall(slow_func);
+    irb_.CreateBr(basic_block_else);
+    irb_.SetInsertPoint(basic_block_else);
+    irb_.CreateRetVoid();
+
+    OverrideRuntimeSupportFunction(TestSuspend, func);
+  }
+}
+
+} // namespace compiler_llvm
+} // namespace art