Implement thin-lock fast path for compiler-llvm.

Change-Id: I09d6a0dba4df7cbeb0c0e3f432ab3b09fce0846d
diff --git a/build/Android.libart-compiler-llvm.mk b/build/Android.libart-compiler-llvm.mk
index f27e83e..e2c66e4 100644
--- a/build/Android.libart-compiler-llvm.mk
+++ b/build/Android.libart-compiler-llvm.mk
@@ -28,6 +28,7 @@
 	src/compiler_llvm/method_compiler.cc \
 	src/compiler_llvm/runtime_support_builder.cc \
 	src/compiler_llvm/runtime_support_builder_arm.cc \
+	src/compiler_llvm/runtime_support_builder_thumb2.cc \
 	src/compiler_llvm/runtime_support_builder_x86.cc \
 	src/compiler_llvm/runtime_support_llvm.cc \
 	src/compiler_llvm/stub_compiler.cc \
diff --git a/src/compiler_llvm/compilation_unit.cc b/src/compiler_llvm/compilation_unit.cc
index 5f239ab..5dc03c3 100644
--- a/src/compiler_llvm/compilation_unit.cc
+++ b/src/compiler_llvm/compilation_unit.cc
@@ -24,6 +24,7 @@
 #include "os.h"
 
 #include "runtime_support_builder_arm.h"
+#include "runtime_support_builder_thumb2.h"
 #include "runtime_support_builder_x86.h"
 
 #include <llvm/ADT/OwningPtr.h>
@@ -180,13 +181,15 @@
 
   // We always need a switch case, so just use a normal function.
   switch(insn_set_) {
-    default:
-      runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
-      break;
+  default:
+    runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
+    break;
   case kArm:
-  case kThumb2:
     runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_));
     break;
+  case kThumb2:
+    runtime_support_.reset(new RuntimeSupportBuilderThumb2(*context_, *module_, *irb_));
+    break;
   case kX86:
     runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_));
     break;
@@ -254,13 +257,13 @@
   switch (insn_set_) {
   case kThumb2:
     target_triple = "thumb-none-linux-gnueabi";
-    target_attr = "+thumb2,+neon,+neonfp,+vfp3";
+    target_attr = "+thumb2,+neon,+neonfp,+vfp3,+db";
     break;
 
   case kArm:
     target_triple = "armv7-none-linux-gnueabi";
     // TODO: Fix for Xoom.
-    target_attr = "+v7,+neon,+neonfp,+vfp3";
+    target_attr = "+v7,+neon,+neonfp,+vfp3,+db";
     break;
 
   case kX86:
diff --git a/src/compiler_llvm/jni_compiler.cc b/src/compiler_llvm/jni_compiler.cc
index 838c89d..bb45fac 100644
--- a/src/compiler_llvm/jni_compiler.cc
+++ b/src/compiler_llvm/jni_compiler.cc
@@ -108,9 +108,6 @@
     }
   }
 
-  // Get thread object
-  llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
   // Shadow stack
   llvm::StructType* shadow_frame_type = irb_.getShadowFrameTy(sirt_size);
   llvm::AllocaInst* shadow_frame_ = irb_.CreateAlloca(shadow_frame_type);
@@ -187,10 +184,7 @@
 
   // Acquire lock for synchronized methods.
   if (is_synchronized) {
-    // Acquire lock
-    irb_.CreateCall2(irb_.GetRuntime(LockObject),
-                     this_object_or_class_object,
-                     thread_object_addr);
+    irb_.Runtime().EmitLockObject(this_object_or_class_object);
   }
 
   // saved_local_ref_cookie = env->local_ref_cookie
@@ -218,9 +212,7 @@
 
   // Release lock for synchronized methods.
   if (is_synchronized) {
-    irb_.CreateCall2(irb_.GetRuntime(UnlockObject),
-                     this_object_or_class_object,
-                     thread_object_addr);
+    irb_.Runtime().EmitUnlockObject(this_object_or_class_object);
   }
 
   // Set thread state to kRunnable
@@ -232,6 +224,9 @@
   irb_.Runtime().EmitTestSuspend();
 
   if (return_shorty == 'L') {
+    // Get thread object
+    llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
+
     // If the return value is reference, it may point to SIRT, we should decode it.
     retval = irb_.CreateCall2(irb_.GetRuntime(DecodeJObjectInThread),
                               thread_object_addr,
diff --git a/src/compiler_llvm/method_compiler.cc b/src/compiler_llvm/method_compiler.cc
index eecace0..4021f7b 100644
--- a/src/compiler_llvm/method_compiler.cc
+++ b/src/compiler_llvm/method_compiler.cc
@@ -1551,9 +1551,7 @@
     EmitGuard_NullPointerException(dex_pc, object_addr);
   }
 
-  llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-  irb_.CreateCall2(irb_.GetRuntime(LockObject), object_addr, thread_object_addr);
+  irb_.Runtime().EmitLockObject(object_addr);
 
   irb_.CreateBr(GetNextBasicBlock(dex_pc));
 }
@@ -1573,9 +1571,7 @@
 
   EmitUpdateDexPC(dex_pc);
 
-  llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-  irb_.CreateCall2(irb_.GetRuntime(UnlockObject), object_addr, thread_object_addr);
+  irb_.Runtime().EmitUnlockObject(object_addr);
 
   EmitGuard_ExceptionLandingPad(dex_pc, true);
 
diff --git a/src/compiler_llvm/runtime_support_builder.cc b/src/compiler_llvm/runtime_support_builder.cc
index b0823d6..9095c02 100644
--- a/src/compiler_llvm/runtime_support_builder.cc
+++ b/src/compiler_llvm/runtime_support_builder.cc
@@ -18,6 +18,8 @@
 
 #include "card_table.h"
 #include "ir_builder.h"
+#include "monitor.h"
+#include "object.h"
 #include "shadow_frame.h"
 #include "thread.h"
 #include "utils_llvm.h"
@@ -179,6 +181,55 @@
 }
 
 
+/* Monitor */
+
+void RuntimeSupportBuilder::EmitLockObject(llvm::Value* object) {
+  // TODO: Implement a fast path.
+  Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
+  irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
+}
+
+void RuntimeSupportBuilder::EmitUnlockObject(llvm::Value* object) {
+  llvm::Value* lock_id =
+      EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(),
+                               irb_.getJIntTy(),
+                               kTBAARuntimeInfo);
+  llvm::Value* monitor =
+      irb_.LoadFromObjectOffset(object,
+                                Object::MonitorOffset().Int32Value(),
+                                irb_.getJIntTy(),
+                                kTBAARuntimeInfo);
+
+  llvm::Value* my_monitor = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT);
+  llvm::Value* hash_state = irb_.CreateAnd(monitor, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+  llvm::Value* real_monitor = irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+
+  // Is thin lock, held by us and not recursively acquired
+  llvm::Value* is_fast_path = irb_.CreateICmpEQ(real_monitor, my_monitor);
+
+  llvm::Function* parent_func = irb_.GetInsertBlock()->getParent();
+  BasicBlock* bb_fast = BasicBlock::Create(context_, "unlock_fast", parent_func);
+  BasicBlock* bb_slow = BasicBlock::Create(context_, "unlock_slow", parent_func);
+  BasicBlock* bb_cont = BasicBlock::Create(context_, "unlock_cont", parent_func);
+  irb_.CreateCondBr(is_fast_path, bb_fast, bb_slow, kLikely);
+
+  irb_.SetInsertPoint(bb_fast);
+  // Set all bits to zero (except hash state)
+  irb_.StoreToObjectOffset(object,
+                           Object::MonitorOffset().Int32Value(),
+                           hash_state,
+                           kTBAARuntimeInfo);
+  irb_.CreateBr(bb_cont);
+
+  irb_.SetInsertPoint(bb_slow);
+  Function* slow_func = GetRuntimeSupportFunction(runtime_support::UnlockObject);
+  irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
+  irb_.CreateBr(bb_cont);
+
+  irb_.SetInsertPoint(bb_cont);
+}
+
+
 void RuntimeSupportBuilder::OptimizeRuntimeSupport() {
   // TODO: Remove this after we remove suspend loop pass.
   if (!target_runtime_support_func_[runtime_support::TestSuspend]) {
diff --git a/src/compiler_llvm/runtime_support_builder.h b/src/compiler_llvm/runtime_support_builder.h
index 89cf30a..7b54c69 100644
--- a/src/compiler_llvm/runtime_support_builder.h
+++ b/src/compiler_llvm/runtime_support_builder.h
@@ -60,6 +60,10 @@
   virtual llvm::Value* EmitIsExceptionPending();
   virtual void EmitTestSuspend();
 
+  /* Monitor */
+  virtual void EmitLockObject(llvm::Value* object);
+  virtual void EmitUnlockObject(llvm::Value* object);
+
   llvm::Function* GetRuntimeSupportFunction(runtime_support::RuntimeId id) {
     if (id >= 0 && id < runtime_support::MAX_ID) {
       return runtime_support_func_decls_[id];
diff --git a/src/compiler_llvm/runtime_support_builder_arm.cc b/src/compiler_llvm/runtime_support_builder_arm.cc
index a180520..6ce9ea4 100644
--- a/src/compiler_llvm/runtime_support_builder_arm.cc
+++ b/src/compiler_llvm/runtime_support_builder_arm.cc
@@ -33,6 +33,7 @@
 namespace art {
 namespace compiler_llvm {
 
+/* Thread */
 
 llvm::Value* RuntimeSupportBuilderARM::EmitGetCurrentThread() {
   Function* ori_func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
@@ -73,5 +74,23 @@
 }
 
 
+/* Monitor */
+
+void RuntimeSupportBuilderARM::EmitLockObject(llvm::Value* object) {
+  RuntimeSupportBuilder::EmitLockObject(object);
+  FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
+                                            /*isVarArg=*/false);
+  InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true);
+  irb_.CreateCall(func);
+}
+
+void RuntimeSupportBuilderARM::EmitUnlockObject(llvm::Value* object) {
+  RuntimeSupportBuilder::EmitUnlockObject(object);
+  FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
+                                            /*isVarArg=*/false);
+  InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true);
+  irb_.CreateCall(func);
+}
+
 } // namespace compiler_llvm
 } // namespace art
diff --git a/src/compiler_llvm/runtime_support_builder_arm.h b/src/compiler_llvm/runtime_support_builder_arm.h
index bf8a589..8828925 100644
--- a/src/compiler_llvm/runtime_support_builder_arm.h
+++ b/src/compiler_llvm/runtime_support_builder_arm.h
@@ -34,6 +34,10 @@
   virtual void EmitStoreToThreadOffset(int64_t offset, llvm::Value* value,
                                        TBAASpecialType s_ty);
   virtual void EmitSetCurrentThread(llvm::Value* thread);
+
+  /* Monitor */
+  virtual void EmitLockObject(llvm::Value* object);
+  virtual void EmitUnlockObject(llvm::Value* object);
 };
 
 } // namespace compiler_llvm
diff --git a/src/compiler_llvm/runtime_support_builder_thumb2.cc b/src/compiler_llvm/runtime_support_builder_thumb2.cc
new file mode 100644
index 0000000..3299afe
--- /dev/null
+++ b/src/compiler_llvm/runtime_support_builder_thumb2.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 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_thumb2.h"
+
+#include "ir_builder.h"
+#include "monitor.h"
+#include "object.h"
+#include "thread.h"
+#include "utils_llvm.h"
+
+#include <llvm/DerivedTypes.h>
+#include <llvm/Function.h>
+#include <llvm/InlineAsm.h>
+#include <llvm/Module.h>
+#include <llvm/Type.h>
+
+#include <inttypes.h>
+#include <vector>
+
+using namespace llvm;
+
+namespace art {
+namespace compiler_llvm {
+
+
+void RuntimeSupportBuilderThumb2::EmitLockObject(llvm::Value* object) {
+  FunctionType* func_ty = FunctionType::get(/*Result=*/irb_.getInt32Ty(),
+                                            /*Params=*/irb_.getJObjectTy(),
+                                            /*isVarArg=*/false);
+  // $0: result
+  // $1: object
+  // $2: temp
+  // $3: temp
+  std::string asms;
+  StringAppendF(&asms, "add $3, $1, #%"PRId32"\n", Object::MonitorOffset().Int32Value());
+  StringAppendF(&asms, "ldr $2, [r9, #%"PRId32"]\n", Thread::ThinLockIdOffset().Int32Value());
+  StringAppendF(&asms, "ldrex $0, [$3]\n");
+  StringAppendF(&asms, "lsl $2, $2, %d\n", LW_LOCK_OWNER_SHIFT);
+  StringAppendF(&asms, "bfi $2, $0, #0, #%d\n", LW_LOCK_OWNER_SHIFT - 1);
+  StringAppendF(&asms, "bfc $0, #%d, #%d\n", LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
+  StringAppendF(&asms, "cmp $0, #0\n");
+  StringAppendF(&asms, "it eq\n");
+  StringAppendF(&asms, "strexeq $0, $2, [$3]\n");
+
+  InlineAsm* func = InlineAsm::get(func_ty, asms, "=&l,l,~l,~l", true);
+
+  llvm::Value* retry_slow_path = irb_.CreateCall(func, object);
+  retry_slow_path = irb_.CreateICmpNE(retry_slow_path, irb_.getJInt(0));
+
+  llvm::Function* parent_func = irb_.GetInsertBlock()->getParent();
+  BasicBlock* basic_block_lock = BasicBlock::Create(context_, "lock", parent_func);
+  BasicBlock* basic_block_cont = BasicBlock::Create(context_, "lock_cont", parent_func);
+  irb_.CreateCondBr(retry_slow_path, basic_block_lock, basic_block_cont, kUnlikely);
+
+  irb_.SetInsertPoint(basic_block_lock);
+  Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
+  irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
+  irb_.CreateBr(basic_block_cont);
+
+  irb_.SetInsertPoint(basic_block_cont);
+  { // Memory barrier
+    FunctionType* asm_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
+                                              /*isVarArg=*/false);
+    InlineAsm* func = InlineAsm::get(asm_ty, "dmb sy", "", true);
+    irb_.CreateCall(func);
+  }
+}
+
+
+} // namespace compiler_llvm
+} // namespace art
diff --git a/src/compiler_llvm/runtime_support_builder_thumb2.h b/src/compiler_llvm/runtime_support_builder_thumb2.h
new file mode 100644
index 0000000..3ac0b9f
--- /dev/null
+++ b/src/compiler_llvm/runtime_support_builder_thumb2.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ART_SRC_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
+#define ART_SRC_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
+
+#include "runtime_support_builder_arm.h"
+
+namespace art {
+namespace compiler_llvm {
+
+class RuntimeSupportBuilderThumb2 : public RuntimeSupportBuilderARM {
+ public:
+  RuntimeSupportBuilderThumb2(llvm::LLVMContext& context, llvm::Module& module, IRBuilder& irb)
+    : RuntimeSupportBuilderARM(context, module, irb) {}
+
+  /* Monitor */
+  virtual void EmitLockObject(llvm::Value* object);
+};
+
+} // namespace compiler_llvm
+} // namespace art
+
+#endif // ART_SRC_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index bf74e06..99f9fee 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -62,7 +62,7 @@
 }
 
 void art_set_current_thread_from_code(void* thread_object_addr) {
-  LOG(FATAL) << "Implemented by IRBuilder.";
+  // Nothing to be done.
 }
 
 void art_lock_object_from_code(Object* obj, Thread* thread) {
diff --git a/src/compiler_llvm/runtime_support_llvm.h b/src/compiler_llvm/runtime_support_llvm.h
index 8f73079..6757ff5 100644
--- a/src/compiler_llvm/runtime_support_llvm.h
+++ b/src/compiler_llvm/runtime_support_llvm.h
@@ -21,9 +21,6 @@
 
 namespace art {
 
-class Method;
-class Object;
-
 //----------------------------------------------------------------------------
 // Thread
 //----------------------------------------------------------------------------