Merge "Inline monitor-enter in portable." into dalvik-dev
diff --git a/src/compiler_llvm/ir_builder.h b/src/compiler_llvm/ir_builder.h
index 4a58515..60a0f2a 100644
--- a/src/compiler_llvm/ir_builder.h
+++ b/src/compiler_llvm/ir_builder.h
@@ -65,6 +65,15 @@
     return inst;
   }
 
+  llvm::AtomicCmpXchgInst*
+  CreateAtomicCmpXchgInst(llvm::Value* ptr, llvm::Value* cmp, llvm::Value* val,
+                          llvm::MDNode* tbaa_info) {
+    llvm::AtomicCmpXchgInst* inst =
+        LLVMIRBuilder::CreateAtomicCmpXchg(ptr, cmp, val, llvm::Acquire);
+    inst->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_info);
+    return inst;
+  }
+
 
   //--------------------------------------------------------------------------
   // TBAA
@@ -120,6 +129,17 @@
     StoreToObjectOffset(object_addr, offset, new_value, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
   }
 
+  llvm::AtomicCmpXchgInst*
+  CompareExchangeObjectOffset(llvm::Value* object_addr,
+                              int64_t offset,
+                              llvm::Value* cmp_value,
+                              llvm::Value* new_value,
+                              TBAASpecialType special_ty) {
+    DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
+    return CompareExchangeObjectOffset(object_addr, offset, cmp_value, new_value,
+                                       mdb_.GetTBAASpecialType(special_ty));
+  }
+
   void SetTBAA(llvm::Instruction* inst, TBAASpecialType special_ty) {
     inst->setMetadata(llvm::LLVMContext::MD_tbaa, mdb_.GetTBAASpecialType(special_ty));
   }
@@ -210,6 +230,21 @@
     CreateStore(new_value, value_addr, tbaa_info);
   }
 
+  llvm::AtomicCmpXchgInst* CompareExchangeObjectOffset(llvm::Value* object_addr,
+                                                       int64_t offset,
+                                                       llvm::Value* cmp_value,
+                                                       llvm::Value* new_value,
+                                                       llvm::MDNode* tbaa_info) {
+    // Convert offset to llvm::value
+    llvm::Value* llvm_offset = getPtrEquivInt(offset);
+    // Calculate the value's address
+    llvm::Value* value_addr = CreatePtrDisp(object_addr,
+                                            llvm_offset,
+                                            new_value->getType()->getPointerTo());
+    // Atomic compare and exchange
+    return CreateAtomicCmpXchgInst(value_addr, cmp_value, new_value, tbaa_info);
+  }
+
 
   //--------------------------------------------------------------------------
   // Runtime Helper Function
diff --git a/src/compiler_llvm/runtime_support_builder.cc b/src/compiler_llvm/runtime_support_builder.cc
index 36b5fa1..e661ec4 100644
--- a/src/compiler_llvm/runtime_support_builder.cc
+++ b/src/compiler_llvm/runtime_support_builder.cc
@@ -167,9 +167,49 @@
 /* Monitor */
 
 void RuntimeSupportBuilder::EmitLockObject(llvm::Value* object) {
-  // TODO: Implement a fast path.
+  Value* monitor =
+      irb_.LoadFromObjectOffset(object,
+                                mirror::Object::MonitorOffset().Int32Value(),
+                                irb_.getJIntTy(),
+                                kTBAARuntimeInfo);
+
+  Value* real_monitor =
+      irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+
+  // Is thin lock, unheld and not recursively acquired.
+  Value* unheld = irb_.CreateICmpEQ(real_monitor, irb_.getInt32(0));
+
+  Function* parent_func = irb_.GetInsertBlock()->getParent();
+  BasicBlock* bb_fast = BasicBlock::Create(context_, "lock_fast", parent_func);
+  BasicBlock* bb_slow = BasicBlock::Create(context_, "lock_slow", parent_func);
+  BasicBlock* bb_cont = BasicBlock::Create(context_, "lock_cont", parent_func);
+  irb_.CreateCondBr(unheld, bb_fast, bb_slow, kLikely);
+
+  irb_.SetInsertPoint(bb_fast);
+
+  // Calculate new monitor: new = old | (lock_id << LW_LOCK_OWNER_SHIFT)
+  Value* lock_id =
+      EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(),
+                               irb_.getInt32Ty(), kTBAARuntimeInfo);
+
+  Value* owner = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT);
+  Value* new_monitor = irb_.CreateOr(monitor, owner);
+
+  // Atomically update monitor.
+  Value* old_monitor =
+      irb_.CompareExchangeObjectOffset(object,
+                                       mirror::Object::MonitorOffset().Int32Value(),
+                                       monitor, new_monitor, kTBAARuntimeInfo);
+
+  Value* retry_slow_path = irb_.CreateICmpEQ(old_monitor, monitor);
+  irb_.CreateCondBr(retry_slow_path, bb_cont, bb_slow, kLikely);
+
+  irb_.SetInsertPoint(bb_slow);
   Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
   irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
+  irb_.CreateBr(bb_cont);
+
+  irb_.SetInsertPoint(bb_cont);
 }
 
 void RuntimeSupportBuilder::EmitUnlockObject(llvm::Value* object) {