Changes to LLVM to support deoptimization.
Added a magic exception value (-1) and a handler to transition
to the interpreter. This is currently untested.
Change-Id: I2f53135e7505c54355ecf7c579897f68bbdcbda3
diff --git a/src/compiler_llvm/gbc_expander.cc b/src/compiler_llvm/gbc_expander.cc
index cc3c3f9..f5d6e8c 100644
--- a/src/compiler_llvm/gbc_expander.cc
+++ b/src/compiler_llvm/gbc_expander.cc
@@ -780,7 +780,19 @@
EmitUpdateDexPC(dex_pc);
}
irb_.Runtime().EmitTestSuspend();
- irb_.CreateBr(basic_block_cont);
+
+ llvm::BasicBlock* basic_block_exception = CreateBasicBlockWithDexPC(dex_pc, "exception");
+ llvm::Value* exception_pending = irb_.Runtime().EmitIsExceptionPending();
+ irb_.CreateCondBr(exception_pending, basic_block_exception, basic_block_cont, kUnlikely);
+
+ irb_.SetInsertPoint(basic_block_exception);
+ llvm::Type* ret_type = call_inst.getParent()->getParent()->getReturnType();
+ if (ret_type->isVoidTy()) {
+ irb_.CreateRetVoid();
+ } else {
+ // The return value is ignored when there's an exception.
+ irb_.CreateRet(llvm::UndefValue::get(ret_type));
+ }
irb_.SetInsertPoint(basic_block_cont);
return;
diff --git a/src/compiler_llvm/runtime_support_llvm.cc b/src/compiler_llvm/runtime_support_llvm.cc
index f32bcde..d0fe4c3 100644
--- a/src/compiler_llvm/runtime_support_llvm.cc
+++ b/src/compiler_llvm/runtime_support_llvm.cc
@@ -38,6 +38,7 @@
#include "thread.h"
#include "thread_list.h"
#include "utils_llvm.h"
+#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
@@ -49,6 +50,57 @@
namespace art {
+class ShadowFrameCopyVisitor : public StackVisitor {
+ public:
+ explicit ShadowFrameCopyVisitor(Thread* self) : StackVisitor(self, NULL), prev_frame_(NULL),
+ top_frame_(NULL) {}
+
+ bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (IsShadowFrame()) {
+ ShadowFrame* cur_frame = GetCurrentShadowFrame();
+ size_t num_regs = cur_frame->NumberOfVRegs();
+ mirror::AbstractMethod* method = cur_frame->GetMethod();
+ uint32_t dex_pc = cur_frame->GetDexPC();
+ ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, method, dex_pc);
+
+ const uint8_t* gc_map = method->GetNativeGcMap();
+ uint32_t gc_map_length = static_cast<uint32_t>((gc_map[0] << 24) |
+ (gc_map[1] << 16) |
+ (gc_map[2] << 8) |
+ (gc_map[3] << 0));
+ verifier::DexPcToReferenceMap dex_gc_map(gc_map + 4, gc_map_length);
+ const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc);
+ for (size_t reg = 0; reg < num_regs; ++reg) {
+ if (TestBitmap(reg, reg_bitmap)) {
+ new_frame->SetVRegReference(reg, cur_frame->GetVRegReference(reg));
+ } else {
+ new_frame->SetVReg(reg, cur_frame->GetVReg(reg));
+ }
+ }
+
+ if (prev_frame_ != NULL) {
+ prev_frame_->SetLink(new_frame);
+ } else {
+ top_frame_ = new_frame;
+ }
+ prev_frame_ = new_frame;
+ }
+ return true;
+ }
+
+ ShadowFrame* GetShadowFrameCopy() {
+ return top_frame_;
+ }
+
+ private:
+ static bool TestBitmap(int reg, const uint8_t* reg_vector) {
+ return ((reg_vector[reg / 8] >> (reg % 8)) & 0x01) != 0;
+ }
+
+ ShadowFrame* prev_frame_;
+ ShadowFrame* top_frame_;
+};
+
//----------------------------------------------------------------------------
// Thread
//----------------------------------------------------------------------------
@@ -85,6 +137,13 @@
void art_test_suspend_from_code(Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CheckSuspend(thread);
+ if (thread->ReadFlag(kEnterInterpreter)) {
+ // Save out the shadow frame to the heap
+ ShadowFrameCopyVisitor visitor(thread);
+ visitor.WalkStack(true);
+ thread->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy(), JValue());
+ thread->SetException(reinterpret_cast<mirror::Throwable*>(-1));
+ }
}
ShadowFrame* art_push_shadow_frame_from_code(Thread* thread, ShadowFrame* new_shadow_frame,
@@ -155,7 +214,12 @@
int32_t art_find_catch_block_from_code(mirror::AbstractMethod* current_method, uint32_t ti_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Class* exception_type = Thread::Current()->GetException()->GetClass();
+ mirror::Throwable* exception = Thread::Current()->GetException();
+ // Check for magic deoptimization exception.
+ if (reinterpret_cast<int32_t>(exception) == -1) {
+ return -1;
+ }
+ mirror::Class* exception_type = exception->GetClass();
MethodHelper mh(current_method);
const DexFile::CodeItem* code_item = mh.GetCodeItem();
DCHECK_LT(ti_offset, code_item->tries_size_);
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index 65729c9..bd63c30 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -1880,6 +1880,21 @@
return Execute(self, mh, code_item, shadow_frame, ret_val);
}
+void EnterInterpreterFromLLVM(Thread* self, ShadowFrame* shadow_frame, JValue* ret_val)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ JValue value;
+ MethodHelper mh(shadow_frame->GetMethod());
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ while (shadow_frame != NULL) {
+ value = Execute(self, mh, code_item, *shadow_frame, value);
+ ShadowFrame* old_frame = shadow_frame;
+ shadow_frame = shadow_frame->GetLink();
+ mh.ChangeMethod(shadow_frame->GetMethod());
+ delete old_frame;
+ }
+ ret_val->SetJ(value.GetJ());
+}
+
JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h
index eee13dc..12da736 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -41,6 +41,9 @@
JValue ret_val)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void EnterInterpreterFromLLVM(Thread* self, ShadowFrame* shadow_frame, JValue* result)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
extern JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame)
diff --git a/src/mirror/abstract_method.cc b/src/mirror/abstract_method.cc
index 4641941..84b8809 100644
--- a/src/mirror/abstract_method.cc
+++ b/src/mirror/abstract_method.cc
@@ -304,6 +304,16 @@
PrettyMethod(this).c_str(), GetCode(), stub);
}
(*stub)(this, receiver, self, args, result);
+ if (UNLIKELY(reinterpret_cast<int32_t>(self->GetException()) == -1)) {
+ // Unusual case where we were running LLVM generated code and an
+ // exception was thrown to force the activations to be removed from the
+ // stack. Continue execution in the interpreter.
+ JValue value;
+ self->ClearException();
+ ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(&value);
+ self->SetTopOfShadowStack(shadow_frame);
+ interpreter::EnterInterpreterFromLLVM(self, shadow_frame, result);
+ }
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf("Returned '%s' code=%p stub=%p",
PrettyMethod(this).c_str(), GetCode(), stub);
diff --git a/src/stack.h b/src/stack.h
index c3d20f5..18a2101 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -315,6 +315,10 @@
return top_shadow_frame_;
}
+ void SetTopShadowFrame(ShadowFrame* top) {
+ top_shadow_frame_ = top;
+ }
+
static size_t TopShadowFrameOffset() {
return OFFSETOF_MEMBER(ManagedStack, top_shadow_frame_);
}
diff --git a/src/thread.h b/src/thread.h
index c63fddf..5e424c1 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -325,6 +325,10 @@
managed_stack_.SetTopQuickFramePc(pc);
}
+ void SetTopOfShadowStack(ShadowFrame* top) {
+ managed_stack_.SetTopShadowFrame(top);
+ }
+
bool HasManagedStack() const {
return managed_stack_.GetTopQuickFrame() != NULL || managed_stack_.GetTopShadowFrame() != NULL;
}