diff options
Diffstat (limited to 'compiler/llvm/runtime_support_builder.cc')
-rw-r--r-- | compiler/llvm/runtime_support_builder.cc | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/compiler/llvm/runtime_support_builder.cc b/compiler/llvm/runtime_support_builder.cc new file mode 100644 index 0000000000..28405f67d4 --- /dev/null +++ b/compiler/llvm/runtime_support_builder.cc @@ -0,0 +1,281 @@ +/* + * 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 "gc/accounting/card_table.h" +#include "ir_builder.h" +#include "monitor.h" +#include "mirror/object.h" +#include "thread.h" + +#include <llvm/IR/DerivedTypes.h> +#include <llvm/IR/Function.h> +#include <llvm/IR/Module.h> +#include <llvm/IR/Type.h> + +using namespace llvm; + +namespace art { +namespace llvm { + +using namespace runtime_support; + + +RuntimeSupportBuilder::RuntimeSupportBuilder(::llvm::LLVMContext& context, + ::llvm::Module& module, + IRBuilder& irb) + : context_(context), module_(module), irb_(irb) +{ + memset(target_runtime_support_func_, 0, sizeof(target_runtime_support_func_)); +#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_[runtime_support::ID] = fn; \ + } while (0); + +#include "runtime_support_llvm_func_list.h" + RUNTIME_SUPPORT_FUNC_LIST(GET_RUNTIME_SUPPORT_FUNC_DECL) +#undef RUNTIME_SUPPORT_FUNC_LIST +#undef GET_RUNTIME_SUPPORT_FUNC_DECL +} + + +/* Thread */ + +::llvm::Value* RuntimeSupportBuilder::EmitGetCurrentThread() { + Function* func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread); + CallInst* call_inst = irb_.CreateCall(func); + call_inst->setOnlyReadsMemory(); + irb_.SetTBAA(call_inst, kTBAAConstJObject); + return call_inst; +} + +::llvm::Value* RuntimeSupportBuilder::EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type, + TBAASpecialType s_ty) { + Value* thread = EmitGetCurrentThread(); + return irb_.LoadFromObjectOffset(thread, offset, type, s_ty); +} + +void RuntimeSupportBuilder::EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value, + TBAASpecialType s_ty) { + Value* thread = EmitGetCurrentThread(); + irb_.StoreToObjectOffset(thread, offset, value, s_ty); +} + +::llvm::Value* RuntimeSupportBuilder::EmitSetCurrentThread(::llvm::Value* thread) { + Function* func = GetRuntimeSupportFunction(runtime_support::SetCurrentThread); + return irb_.CreateCall(func, thread); +} + + +/* ShadowFrame */ + +::llvm::Value* RuntimeSupportBuilder::EmitPushShadowFrame(::llvm::Value* new_shadow_frame, + ::llvm::Value* method, + uint32_t num_vregs) { + Value* old_shadow_frame = EmitLoadFromThreadOffset(Thread::TopShadowFrameOffset().Int32Value(), + irb_.getArtFrameTy()->getPointerTo(), + kTBAARuntimeInfo); + EmitStoreToThreadOffset(Thread::TopShadowFrameOffset().Int32Value(), + new_shadow_frame, + kTBAARuntimeInfo); + + // Store the method pointer + irb_.StoreToObjectOffset(new_shadow_frame, + ShadowFrame::MethodOffset(), + method, + kTBAAShadowFrame); + + // Store the number of vregs + irb_.StoreToObjectOffset(new_shadow_frame, + ShadowFrame::NumberOfVRegsOffset(), + irb_.getInt32(num_vregs), + kTBAAShadowFrame); + + // Store the link to previous shadow frame + irb_.StoreToObjectOffset(new_shadow_frame, + ShadowFrame::LinkOffset(), + old_shadow_frame, + kTBAAShadowFrame); + + return old_shadow_frame; +} + +::llvm::Value* +RuntimeSupportBuilder::EmitPushShadowFrameNoInline(::llvm::Value* new_shadow_frame, + ::llvm::Value* method, + uint32_t num_vregs) { + Function* func = GetRuntimeSupportFunction(runtime_support::PushShadowFrame); + ::llvm::CallInst* call_inst = + irb_.CreateCall4(func, + EmitGetCurrentThread(), + new_shadow_frame, + method, + irb_.getInt32(num_vregs)); + irb_.SetTBAA(call_inst, kTBAARuntimeInfo); + return call_inst; +} + +void RuntimeSupportBuilder::EmitPopShadowFrame(::llvm::Value* old_shadow_frame) { + // Store old shadow frame to TopShadowFrame + EmitStoreToThreadOffset(Thread::TopShadowFrameOffset().Int32Value(), + old_shadow_frame, + kTBAARuntimeInfo); +} + + +/* Exception */ + +::llvm::Value* RuntimeSupportBuilder::EmitGetAndClearException() { + Function* slow_func = GetRuntimeSupportFunction(runtime_support::GetAndClearException); + return irb_.CreateCall(slow_func, EmitGetCurrentThread()); +} + +::llvm::Value* RuntimeSupportBuilder::EmitIsExceptionPending() { + Value* exception = EmitLoadFromThreadOffset(Thread::ExceptionOffset().Int32Value(), + irb_.getJObjectTy(), + kTBAARuntimeInfo); + // If exception not null + return irb_.CreateIsNotNull(exception); +} + + +/* Suspend */ + +void RuntimeSupportBuilder::EmitTestSuspend() { + Function* slow_func = GetRuntimeSupportFunction(runtime_support::TestSuspend); + CallInst* call_inst = irb_.CreateCall(slow_func, EmitGetCurrentThread()); + irb_.SetTBAA(call_inst, kTBAAJRuntime); +} + + +/* Monitor */ + +void RuntimeSupportBuilder::EmitLockObject(::llvm::Value* object) { + 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) { + Value* lock_id = + EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(), + irb_.getJIntTy(), + kTBAARuntimeInfo); + Value* monitor = + irb_.LoadFromObjectOffset(object, + mirror::Object::MonitorOffset().Int32Value(), + irb_.getJIntTy(), + kTBAARuntimeInfo); + + Value* my_monitor = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT); + Value* hash_state = irb_.CreateAnd(monitor, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT)); + Value* real_monitor = irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT)); + + // Is thin lock, held by us and not recursively acquired + Value* is_fast_path = irb_.CreateICmpEQ(real_monitor, my_monitor); + + 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, + mirror::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::EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr) { + Function* parent_func = irb_.GetInsertBlock()->getParent(); + BasicBlock* bb_mark_gc_card = BasicBlock::Create(context_, "mark_gc_card", parent_func); + BasicBlock* bb_cont = BasicBlock::Create(context_, "mark_gc_card_cont", parent_func); + + ::llvm::Value* not_null = irb_.CreateIsNotNull(value); + irb_.CreateCondBr(not_null, bb_mark_gc_card, bb_cont); + + irb_.SetInsertPoint(bb_mark_gc_card); + Value* card_table = EmitLoadFromThreadOffset(Thread::CardTableOffset().Int32Value(), + irb_.getInt8Ty()->getPointerTo(), + kTBAAConstJObject); + Value* target_addr_int = irb_.CreatePtrToInt(target_addr, irb_.getPtrEquivIntTy()); + Value* card_no = irb_.CreateLShr(target_addr_int, + irb_.getPtrEquivInt(gc::accounting::CardTable::kCardShift)); + Value* card_table_entry = irb_.CreateGEP(card_table, card_no); + irb_.CreateStore(irb_.getInt8(gc::accounting::CardTable::kCardDirty), card_table_entry, + kTBAARuntimeInfo); + irb_.CreateBr(bb_cont); + + irb_.SetInsertPoint(bb_cont); +} + + +} // namespace llvm +} // namespace art |