Lazy pushing shadow frame.

Function:
aget v0, v1, v2
return v0

Original:
push shadow frame;
if (null pointer)        { throw; pop shadow frame; unwind; }
if (index out of bounds) { throw; pop shadow frame; unwind; }
load from array;
pop shadow frame;
return;

New:
if (null pointer)        { push shadow frame; throw; pop shadow frame; unwind; }
if (index out of bounds) { push shadow frame; throw; pop shadow frame; unwind; }
load from array;
return;

Change-Id: I7fc0ff12b9a5454f8e1491b9ce1cdef3afcbed23
diff --git a/src/compiler_llvm/art_module.ll b/src/compiler_llvm/art_module.ll
index e7738ad..cebc76c 100644
--- a/src/compiler_llvm/art_module.ll
+++ b/src/compiler_llvm/art_module.ll
@@ -43,7 +43,8 @@
 
 declare void @art_test_suspend_from_code(%JavaObject*)
 
-declare %ShadowFrame* @art_push_shadow_frame_from_code(%ShadowFrame*)
+declare %ShadowFrame* @art_push_shadow_frame_from_code(%ShadowFrame*, %JavaObject*, i32)
+declare %ShadowFrame* @art_push_shadow_frame_noinline_from_code(%ShadowFrame*, %JavaObject*, i32)
 declare void @art_pop_shadow_frame_from_code(%ShadowFrame*)
 
 
diff --git a/src/compiler_llvm/generated/art_module.cc b/src/compiler_llvm/generated/art_module.cc
index f25a88b..1f88e8d 100644
--- a/src/compiler_llvm/generated/art_module.cc
+++ b/src/compiler_llvm/generated/art_module.cc
@@ -84,6 +84,8 @@
 
 std::vector<Type*>FuncTy_6_args;
 FuncTy_6_args.push_back(PointerTy_2);
+FuncTy_6_args.push_back(PointerTy_1);
+FuncTy_6_args.push_back(IntegerType::get(mod->getContext(), 32));
 FunctionType* FuncTy_6 = FunctionType::get(
  /*Result=*/PointerTy_2,
  /*Params=*/FuncTy_6_args,
@@ -423,6 +425,17 @@
 AttrListPtr func_art_push_shadow_frame_from_code_PAL;
 func_art_push_shadow_frame_from_code->setAttributes(func_art_push_shadow_frame_from_code_PAL);
 
+Function* func_art_push_shadow_frame_noinline_from_code = mod->getFunction("art_push_shadow_frame_noinline_from_code");
+if (!func_art_push_shadow_frame_noinline_from_code) {
+func_art_push_shadow_frame_noinline_from_code = Function::Create(
+ /*Type=*/FuncTy_6,
+ /*Linkage=*/GlobalValue::ExternalLinkage,
+ /*Name=*/"art_push_shadow_frame_noinline_from_code", mod); // (external, no body)
+func_art_push_shadow_frame_noinline_from_code->setCallingConv(CallingConv::C);
+}
+AttrListPtr func_art_push_shadow_frame_noinline_from_code_PAL;
+func_art_push_shadow_frame_noinline_from_code->setAttributes(func_art_push_shadow_frame_noinline_from_code_PAL);
+
 Function* func_art_pop_shadow_frame_from_code = mod->getFunction("art_pop_shadow_frame_from_code");
 if (!func_art_pop_shadow_frame_from_code) {
 func_art_pop_shadow_frame_from_code = Function::Create(
diff --git a/src/compiler_llvm/jni_compiler.cc b/src/compiler_llvm/jni_compiler.cc
index c76c80d..e66674c 100644
--- a/src/compiler_llvm/jni_compiler.cc
+++ b/src/compiler_llvm/jni_compiler.cc
@@ -115,29 +115,17 @@
   llvm::StructType* shadow_frame_type = irb_.getShadowFrameTy(sirt_size);
   llvm::AllocaInst* shadow_frame_ = irb_.CreateAlloca(shadow_frame_type);
 
-  // Store the method pointer
-  llvm::Value* method_field_addr =
-    irb_.CreatePtrDisp(shadow_frame_,
-                       irb_.getPtrEquivInt(ShadowFrame::MethodOffset()),
-                       irb_.getJObjectTy()->getPointerTo());
-  irb_.CreateStore(method_object_addr, method_field_addr, kTBAAShadowFrame);
-
   // Store the dex pc
   irb_.StoreToObjectOffset(shadow_frame_,
                            ShadowFrame::DexPCOffset(),
                            irb_.getInt32(0),
                            kTBAAShadowFrame);
 
-  // Store the number of the pointer slots
-  irb_.StoreToObjectOffset(shadow_frame_,
-                           ShadowFrame::NumberOfReferencesOffset(),
-                           irb_.getInt32(sirt_size),
-                           kTBAAShadowFrame);
-
   // Push the shadow frame
   llvm::Value* shadow_frame_upcast = irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
   llvm::Value* old_shadow_frame =
-      irb_.CreateCall(irb_.GetRuntime(PushShadowFrame), shadow_frame_upcast);
+      irb_.CreateCall3(irb_.GetRuntime(PushShadowFrame),
+                       shadow_frame_upcast, method_object_addr, irb_.getInt32(sirt_size));
 
   // Get JNIEnv
   llvm::Value* jni_env_object_addr =
diff --git a/src/compiler_llvm/method_compiler.cc b/src/compiler_llvm/method_compiler.cc
index 0612855..bdad82b 100644
--- a/src/compiler_llvm/method_compiler.cc
+++ b/src/compiler_llvm/method_compiler.cc
@@ -73,6 +73,7 @@
     basic_block_landing_pads_(code_item_->tries_size_, NULL),
     basic_block_unwind_(NULL), basic_block_unreachable_(NULL),
     shadow_frame_(NULL), jvalue_temp_(NULL), old_shadow_frame_(NULL),
+    already_pushed_shadow_frame_(NULL), shadow_frame_size_(0),
     elf_func_idx_(cunit_->AcquireUniqueElfFuncIndex()) {
 }
 
@@ -277,18 +278,21 @@
   irb_.SetInsertPoint(basic_block_alloca_);
 
   // Allocate the shadow frame now!
-  uint32_t sirt_size = 0;
+  shadow_frame_size_ = 0;
   if (method_info_.need_shadow_frame_entry) {
     for (uint32_t i = 0, num_of_regs = code_item_->registers_size_; i < num_of_regs; ++i) {
       if (IsRegCanBeObject(i)) {
-        reg_to_shadow_frame_index_[i] = sirt_size++;
+        reg_to_shadow_frame_index_[i] = shadow_frame_size_++;
       }
     }
   }
 
-  llvm::StructType* shadow_frame_type = irb_.getShadowFrameTy(sirt_size);
+  llvm::StructType* shadow_frame_type = irb_.getShadowFrameTy(shadow_frame_size_);
   shadow_frame_ = irb_.CreateAlloca(shadow_frame_type);
 
+  // Alloca a pointer to old shadow frame
+  old_shadow_frame_ = irb_.CreateAlloca(shadow_frame_type->getElementType(0)->getPointerTo());
+
   irb_.SetInsertPoint(basic_block_shadow_frame_);
 
   // Zero-initialization of the shadow frame table
@@ -300,26 +304,16 @@
 
   irb_.CreateStore(zero_initializer, shadow_frame_table, kTBAAShadowFrame);
 
-  // Get method object
-  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
+  // Lazy pushing shadow frame
+  if (method_info_.lazy_push_shadow_frame) {
+    irb_.SetInsertPoint(basic_block_alloca_);
+    already_pushed_shadow_frame_ = irb_.CreateAlloca(irb_.getInt1Ty());
+    irb_.SetInsertPoint(basic_block_shadow_frame_);
+    irb_.CreateStore(irb_.getFalse(), already_pushed_shadow_frame_, kTBAARegister);
+    return;
+  }
 
-  // Store the method pointer
-  irb_.StoreToObjectOffset(shadow_frame_,
-                           ShadowFrame::MethodOffset(),
-                           method_object_addr,
-                           kTBAAShadowFrame);
-
-  // Store the number of the pointer slots
-  irb_.StoreToObjectOffset(shadow_frame_,
-                           ShadowFrame::NumberOfReferencesOffset(),
-                           irb_.getJInt(sirt_size),
-                           kTBAAShadowFrame);
-
-  // Push the shadow frame
-  llvm::Value* shadow_frame_upcast =
-    irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
-
-  old_shadow_frame_ = irb_.CreateCall(irb_.GetRuntime(PushShadowFrame), shadow_frame_upcast);
+  EmitPushShadowFrame(true);
 }
 
 
@@ -3952,12 +3946,50 @@
 }
 
 
+void MethodCompiler::EmitPushShadowFrame(bool is_inline) {
+  if (!method_info_.need_shadow_frame) {
+    return;
+  }
+  DCHECK(shadow_frame_ != NULL);
+  DCHECK(old_shadow_frame_ != NULL);
+
+  // Get method object
+  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
+
+  // Push the shadow frame
+  llvm::Value* shadow_frame_upcast =
+    irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
+
+  llvm::Value* result =
+      irb_.CreateCall3(irb_.GetRuntime(is_inline ? PushShadowFrame : PushShadowFrameNoInline),
+                       shadow_frame_upcast, method_object_addr, irb_.getJInt(shadow_frame_size_));
+  irb_.CreateStore(result, old_shadow_frame_, kTBAARegister);
+}
+
+
 void MethodCompiler::EmitPopShadowFrame() {
   if (!method_info_.need_shadow_frame) {
     return;
   }
   DCHECK(old_shadow_frame_ != NULL);
-  irb_.CreateCall(irb_.GetRuntime(PopShadowFrame), old_shadow_frame_);
+
+  if (method_info_.lazy_push_shadow_frame) {
+    llvm::BasicBlock* bb_pop = llvm::BasicBlock::Create(*context_, "pop", func_);
+    llvm::BasicBlock* bb_cont = llvm::BasicBlock::Create(*context_, "cont", func_);
+
+    llvm::Value* need_pop = irb_.CreateLoad(already_pushed_shadow_frame_, kTBAARegister);
+    irb_.CreateCondBr(need_pop, bb_pop, bb_cont, kUnlikely);
+
+    irb_.SetInsertPoint(bb_pop);
+    irb_.CreateCall(irb_.GetRuntime(PopShadowFrame),
+                    irb_.CreateLoad(old_shadow_frame_, kTBAARegister));
+    irb_.CreateBr(bb_cont);
+
+    irb_.SetInsertPoint(bb_cont);
+  } else {
+    irb_.CreateCall(irb_.GetRuntime(PopShadowFrame),
+                    irb_.CreateLoad(old_shadow_frame_, kTBAARegister));
+  }
 }
 
 
@@ -3969,6 +4001,21 @@
                            ShadowFrame::DexPCOffset(),
                            irb_.getInt32(dex_pc),
                            kTBAAShadowFrame);
+  // Lazy pushing shadow frame
+  if (method_info_.lazy_push_shadow_frame) {
+    llvm::BasicBlock* bb_push = CreateBasicBlockWithDexPC(dex_pc, "push");
+    llvm::BasicBlock* bb_cont = CreateBasicBlockWithDexPC(dex_pc, "cont");
+
+    llvm::Value* no_need_push = irb_.CreateLoad(already_pushed_shadow_frame_, kTBAARegister);
+    irb_.CreateCondBr(no_need_push, bb_cont, bb_push, kLikely);
+
+    irb_.SetInsertPoint(bb_push);
+    EmitPushShadowFrame(false);
+    irb_.CreateStore(irb_.getTrue(), already_pushed_shadow_frame_, kTBAARegister);
+    irb_.CreateBr(bb_cont);
+
+    irb_.SetInsertPoint(bb_cont);
+  }
 }
 
 
@@ -4611,6 +4658,10 @@
   method_info_.need_shadow_frame_entry = has_invoke || may_have_loop;
   // If this method may throw an exception, we need a shadow frame for stack trace (dexpc).
   method_info_.need_shadow_frame = method_info_.need_shadow_frame_entry || may_throw_exception;
+  // If can only throw exception, but can't suspend check (no loop, no invoke),
+  // then there is no shadow frame entry. Only Shadow frame is needed.
+  method_info_.lazy_push_shadow_frame =
+      method_info_.need_shadow_frame && !method_info_.need_shadow_frame_entry;
 }
 
 
diff --git a/src/compiler_llvm/method_compiler.h b/src/compiler_llvm/method_compiler.h
index 9e4c81b..44ab863 100644
--- a/src/compiler_llvm/method_compiler.h
+++ b/src/compiler_llvm/method_compiler.h
@@ -264,6 +264,7 @@
   void EmitMarkGCCard(llvm::Value* value, llvm::Value* target_addr);
 
   // Shadow frame helper function
+  void EmitPushShadowFrame(bool is_inline);
   void EmitPopShadowFrame();
   void EmitUpdateDexPC(uint32_t dex_pc);
 
@@ -430,6 +431,7 @@
     bool has_invoke;
     bool need_shadow_frame_entry;
     bool need_shadow_frame;
+    bool lazy_push_shadow_frame;
   };
   MethodInfo method_info_;
 
@@ -474,6 +476,9 @@
   llvm::AllocaInst* jvalue_temp_;
   llvm::Value* old_shadow_frame_;
 
+  llvm::Value* already_pushed_shadow_frame_;
+  uint32_t shadow_frame_size_;
+
   uint16_t elf_func_idx_;
 };
 
diff --git a/src/compiler_llvm/runtime_support_builder.cc b/src/compiler_llvm/runtime_support_builder.cc
index 3bcd212..2e86938 100644
--- a/src/compiler_llvm/runtime_support_builder.cc
+++ b/src/compiler_llvm/runtime_support_builder.cc
@@ -80,7 +80,10 @@
 
     Function* get_thread = GetRuntimeSupportFunction(GetCurrentThread);
     Value* thread = irb_.CreateCall(get_thread);
-    Value* new_shadow_frame = func->arg_begin();
+    Function::arg_iterator arg_iter = func->arg_begin();
+    Value* new_shadow_frame = arg_iter++;
+    Value* method_object_addr = arg_iter++;
+    Value* shadow_frame_size = arg_iter++;
     Value* old_shadow_frame = irb_.LoadFromObjectOffset(thread,
                                                         Thread::TopShadowFrameOffset().Int32Value(),
                                                         irb_.getArtFrameTy()->getPointerTo(),
@@ -89,10 +92,48 @@
                              Thread::TopShadowFrameOffset().Int32Value(),
                              new_shadow_frame,
                              kTBAARuntimeInfo);
+
+    // Store the method pointer
+    irb_.StoreToObjectOffset(new_shadow_frame,
+                             ShadowFrame::MethodOffset(),
+                             method_object_addr,
+                             kTBAAShadowFrame);
+
+    // Store the number of the pointer slots
+    irb_.StoreToObjectOffset(new_shadow_frame,
+                             ShadowFrame::NumberOfReferencesOffset(),
+                             shadow_frame_size,
+                             kTBAAShadowFrame);
+
+    // Store the link to previous shadow frame
     irb_.StoreToObjectOffset(new_shadow_frame,
                              ShadowFrame::LinkOffset(),
                              old_shadow_frame,
                              kTBAAShadowFrame);
+
+    irb_.CreateRet(old_shadow_frame);
+
+    VERIFY_LLVM_FUNCTION(*func);
+  }
+
+  if (!target_runtime_support_func_[PushShadowFrameNoInline]) {
+    Function* func = GetRuntimeSupportFunction(PushShadowFrameNoInline);
+
+    func->setLinkage(GlobalValue::PrivateLinkage);
+    func->addFnAttr(Attribute::NoInline);
+
+    BasicBlock* basic_block = BasicBlock::Create(context_, "entry", func);
+    irb_.SetInsertPoint(basic_block);
+
+    Function::arg_iterator arg_iter = func->arg_begin();
+    Value* new_shadow_frame = arg_iter++;
+    Value* method_object_addr = arg_iter++;
+    Value* shadow_frame_size = arg_iter++;
+
+    // Call inline version
+    Value* old_shadow_frame =
+      irb_.CreateCall3(GetRuntimeSupportFunction(PushShadowFrame),
+                       new_shadow_frame, method_object_addr, shadow_frame_size);
     irb_.CreateRet(old_shadow_frame);
 
     VERIFY_LLVM_FUNCTION(*func);
diff --git a/src/compiler_llvm/runtime_support_func_list.h b/src/compiler_llvm/runtime_support_func_list.h
index 62729c6..74af5da 100644
--- a/src/compiler_llvm/runtime_support_func_list.h
+++ b/src/compiler_llvm/runtime_support_func_list.h
@@ -20,6 +20,7 @@
   V(GetCurrentThread, art_get_current_thread_from_code) \
   V(SetCurrentThread, art_set_current_thread_from_code) \
   V(PushShadowFrame, art_push_shadow_frame_from_code) \
+  V(PushShadowFrameNoInline, art_push_shadow_frame_noinline_from_code) \
   V(PopShadowFrame, art_pop_shadow_frame_from_code) \
   V(TestSuspend, art_test_suspend_from_code) \
   V(ThrowException, art_throw_exception_from_code) \
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index 86bf954..08e5921 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -80,7 +80,13 @@
   Runtime::Current()->GetThreadList()->FullSuspendCheck(thread);
 }
 
-void* art_push_shadow_frame_from_code(void* new_shadow_frame) {
+void* art_push_shadow_frame_from_code(void* new_shadow_frame, Object* method, uint32_t size) {
+  LOG(FATAL) << "Implemented by IRBuilder.";
+  return NULL;
+}
+
+void* art_push_shadow_frame_noinline_from_code(void* new_shadow_frame,
+                                               Object* method, uint32_t size) {
   LOG(FATAL) << "Implemented by IRBuilder.";
   return NULL;
 }
diff --git a/src/compiler_llvm/runtime_support_llvm.h b/src/compiler_llvm/runtime_support_llvm.h
index b7f0655..5647c54 100644
--- a/src/compiler_llvm/runtime_support_llvm.h
+++ b/src/compiler_llvm/runtime_support_llvm.h
@@ -36,7 +36,10 @@
 // Thread
 //----------------------------------------------------------------------------
 
-void* art_push_shadow_frame_from_code(void* new_shadow_frame);
+void* art_push_shadow_frame_from_code(void* new_shadow_frame, Object* method, uint32_t size);
+
+void* art_push_shadow_frame_noinline_from_code(void* new_shadow_frame,
+                                               Object* method, uint32_t size);
 
 void art_pop_shadow_frame_from_code(void*);