diff options
Diffstat (limited to 'compiler/llvm/gbc_expander.cc')
-rw-r--r-- | compiler/llvm/gbc_expander.cc | 3753 |
1 files changed, 3753 insertions, 0 deletions
diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc new file mode 100644 index 0000000..b139e32 --- /dev/null +++ b/compiler/llvm/gbc_expander.cc @@ -0,0 +1,3753 @@ +/* + * 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 "dex_file-inl.h" +#include "driver/compiler_driver.h" +#include "driver/dex_compilation_unit.h" +#include "intrinsic_helper.h" +#include "ir_builder.h" +#include "method_reference.h" +#include "mirror/abstract_method.h" +#include "mirror/array.h" +#include "mirror/string.h" +#include "thread.h" +#include "utils_llvm.h" +#include "verifier/method_verifier.h" + +#include "dex/compiler_ir.h" +#include "dex/mir_graph.h" +#include "dex/quick/mir_to_lir.h" +using art::kMIRIgnoreNullCheck; +using art::kMIRIgnoreRangeCheck; + +#include <llvm/ADT/STLExtras.h> +#include <llvm/IR/Intrinsics.h> +#include <llvm/IR/Metadata.h> +#include <llvm/Pass.h> +#include <llvm/Support/CFG.h> +#include <llvm/Support/InstIterator.h> + +#include <vector> +#include <map> +#include <utility> + +using namespace art::llvm; + +using art::llvm::IntrinsicHelper; + +namespace art { +extern char RemapShorty(char shortyType); +}; + +namespace { + +class GBCExpanderPass : public llvm::FunctionPass { + private: + const IntrinsicHelper& intrinsic_helper_; + IRBuilder& irb_; + + llvm::LLVMContext& context_; + RuntimeSupportBuilder& rtb_; + + private: + llvm::AllocaInst* shadow_frame_; + llvm::Value* old_shadow_frame_; + + private: + art::CompilerDriver* const driver_; + + const art::DexCompilationUnit* const dex_compilation_unit_; + + llvm::Function* func_; + + std::vector<llvm::BasicBlock*> basic_blocks_; + + std::vector<llvm::BasicBlock*> basic_block_landing_pads_; + llvm::BasicBlock* current_bb_; + std::map<llvm::BasicBlock*, std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock*> > > + landing_pad_phi_mapping_; + llvm::BasicBlock* basic_block_unwind_; + + // Maps each vreg to its shadow frame address. + std::vector<llvm::Value*> shadow_frame_vreg_addresses_; + + bool changed_; + + private: + //---------------------------------------------------------------------------- + // Constant for GBC expansion + //---------------------------------------------------------------------------- + enum IntegerShiftKind { + kIntegerSHL, + kIntegerSHR, + kIntegerUSHR, + }; + + private: + //---------------------------------------------------------------------------- + // Helper function for GBC expansion + //---------------------------------------------------------------------------- + + llvm::Value* ExpandToRuntime(runtime_support::RuntimeId rt, + llvm::CallInst& inst); + + uint64_t LV2UInt(llvm::Value* lv) { + return llvm::cast<llvm::ConstantInt>(lv)->getZExtValue(); + } + + int64_t LV2SInt(llvm::Value* lv) { + return llvm::cast<llvm::ConstantInt>(lv)->getSExtValue(); + } + + private: + // TODO: Almost all Emit* are directly copy-n-paste from MethodCompiler. + // Refactor these utility functions from MethodCompiler to avoid forking. + + void EmitStackOverflowCheck(llvm::Instruction* first_non_alloca); + + void RewriteFunction(); + + void RewriteBasicBlock(llvm::BasicBlock* original_block); + + void UpdatePhiInstruction(llvm::BasicBlock* old_basic_block, + llvm::BasicBlock* new_basic_block); + + + // Sign or zero extend category 1 types < 32bits in size to 32bits. + llvm::Value* SignOrZeroExtendCat1Types(llvm::Value* value, JType jty); + + // Truncate category 1 types from 32bits to the given JType size. + llvm::Value* TruncateCat1Types(llvm::Value* value, JType jty); + + //---------------------------------------------------------------------------- + // Dex cache code generation helper function + //---------------------------------------------------------------------------- + llvm::Value* EmitLoadDexCacheAddr(art::MemberOffset dex_cache_offset); + + llvm::Value* EmitLoadDexCacheStaticStorageFieldAddr(uint32_t type_idx); + + llvm::Value* EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx); + + llvm::Value* EmitLoadDexCacheResolvedMethodFieldAddr(uint32_t method_idx); + + llvm::Value* EmitLoadDexCacheStringFieldAddr(uint32_t string_idx); + + //---------------------------------------------------------------------------- + // Code generation helper function + //---------------------------------------------------------------------------- + llvm::Value* EmitLoadMethodObjectAddr(); + + llvm::Value* EmitLoadArrayLength(llvm::Value* array); + + llvm::Value* EmitLoadSDCalleeMethodObjectAddr(uint32_t callee_method_idx); + + llvm::Value* EmitLoadVirtualCalleeMethodObjectAddr(int vtable_idx, + llvm::Value* this_addr); + + llvm::Value* EmitArrayGEP(llvm::Value* array_addr, + llvm::Value* index_value, + JType elem_jty); + + //---------------------------------------------------------------------------- + // Invoke helper function + //---------------------------------------------------------------------------- + llvm::Value* EmitInvoke(llvm::CallInst& call_inst); + + //---------------------------------------------------------------------------- + // Inlining helper functions + //---------------------------------------------------------------------------- + bool EmitIntrinsic(llvm::CallInst& call_inst, llvm::Value** result); + + bool EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst, + llvm::Value** result, bool is_empty); + + private: + //---------------------------------------------------------------------------- + // Expand Greenland intrinsics + //---------------------------------------------------------------------------- + void Expand_TestSuspend(llvm::CallInst& call_inst); + + void Expand_MarkGCCard(llvm::CallInst& call_inst); + + llvm::Value* Expand_LoadStringFromDexCache(llvm::Value* string_idx_value); + + llvm::Value* Expand_LoadTypeFromDexCache(llvm::Value* type_idx_value); + + void Expand_LockObject(llvm::Value* obj); + + void Expand_UnlockObject(llvm::Value* obj); + + llvm::Value* Expand_ArrayGet(llvm::Value* array_addr, + llvm::Value* index_value, + JType elem_jty); + + void Expand_ArrayPut(llvm::Value* new_value, + llvm::Value* array_addr, + llvm::Value* index_value, + JType elem_jty); + + void Expand_FilledNewArray(llvm::CallInst& call_inst); + + llvm::Value* Expand_IGetFast(llvm::Value* field_offset_value, + llvm::Value* is_volatile_value, + llvm::Value* object_addr, + JType field_jty); + + void Expand_IPutFast(llvm::Value* field_offset_value, + llvm::Value* is_volatile_value, + llvm::Value* object_addr, + llvm::Value* new_value, + JType field_jty); + + llvm::Value* Expand_SGetFast(llvm::Value* static_storage_addr, + llvm::Value* field_offset_value, + llvm::Value* is_volatile_value, + JType field_jty); + + void Expand_SPutFast(llvm::Value* static_storage_addr, + llvm::Value* field_offset_value, + llvm::Value* is_volatile_value, + llvm::Value* new_value, + JType field_jty); + + llvm::Value* Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr); + + llvm::Value* Expand_LoadClassSSBFromDexCache(llvm::Value* type_idx_value); + + llvm::Value* + Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value); + + llvm::Value* + Expand_GetVirtualCalleeMethodObjAddrFast(llvm::Value* vtable_idx_value, + llvm::Value* this_addr); + + llvm::Value* Expand_Invoke(llvm::CallInst& call_inst); + + llvm::Value* Expand_DivRem(llvm::CallInst& call_inst, bool is_div, JType op_jty); + + void Expand_AllocaShadowFrame(llvm::Value* num_vregs_value); + + void Expand_SetVReg(llvm::Value* entry_idx, llvm::Value* obj); + + void Expand_PopShadowFrame(); + + void Expand_UpdateDexPC(llvm::Value* dex_pc_value); + + //---------------------------------------------------------------------------- + // Quick + //---------------------------------------------------------------------------- + + llvm::Value* Expand_FPCompare(llvm::Value* src1_value, + llvm::Value* src2_value, + bool gt_bias); + + llvm::Value* Expand_LongCompare(llvm::Value* src1_value, llvm::Value* src2_value); + + llvm::Value* EmitCompareResultSelection(llvm::Value* cmp_eq, + llvm::Value* cmp_lt); + + llvm::Value* EmitLoadConstantClass(uint32_t dex_pc, uint32_t type_idx); + llvm::Value* EmitLoadStaticStorage(uint32_t dex_pc, uint32_t type_idx); + + llvm::Value* Expand_HLIGet(llvm::CallInst& call_inst, JType field_jty); + void Expand_HLIPut(llvm::CallInst& call_inst, JType field_jty); + + llvm::Value* Expand_HLSget(llvm::CallInst& call_inst, JType field_jty); + void Expand_HLSput(llvm::CallInst& call_inst, JType field_jty); + + llvm::Value* Expand_HLArrayGet(llvm::CallInst& call_inst, JType field_jty); + void Expand_HLArrayPut(llvm::CallInst& call_inst, JType field_jty); + + llvm::Value* Expand_ConstString(llvm::CallInst& call_inst); + llvm::Value* Expand_ConstClass(llvm::CallInst& call_inst); + + void Expand_MonitorEnter(llvm::CallInst& call_inst); + void Expand_MonitorExit(llvm::CallInst& call_inst); + + void Expand_HLCheckCast(llvm::CallInst& call_inst); + llvm::Value* Expand_InstanceOf(llvm::CallInst& call_inst); + + llvm::Value* Expand_NewInstance(llvm::CallInst& call_inst); + + llvm::Value* Expand_HLInvoke(llvm::CallInst& call_inst); + + llvm::Value* Expand_OptArrayLength(llvm::CallInst& call_inst); + llvm::Value* Expand_NewArray(llvm::CallInst& call_inst); + llvm::Value* Expand_HLFilledNewArray(llvm::CallInst& call_inst); + void Expand_HLFillArrayData(llvm::CallInst& call_inst); + + llvm::Value* EmitAllocNewArray(uint32_t dex_pc, + llvm::Value* array_length_value, + uint32_t type_idx, + bool is_filled_new_array); + + llvm::Value* EmitCallRuntimeForCalleeMethodObjectAddr(uint32_t callee_method_idx, + art::InvokeType invoke_type, + llvm::Value* this_addr, + uint32_t dex_pc, + bool is_fast_path); + + void EmitMarkGCCard(llvm::Value* value, llvm::Value* target_addr); + + void EmitUpdateDexPC(uint32_t dex_pc); + + void EmitGuard_DivZeroException(uint32_t dex_pc, + llvm::Value* denominator, + JType op_jty); + + void EmitGuard_NullPointerException(uint32_t dex_pc, llvm::Value* object, + int opt_flags); + + void EmitGuard_ArrayIndexOutOfBoundsException(uint32_t dex_pc, + llvm::Value* array, + llvm::Value* index, + int opt_flags); + + llvm::FunctionType* GetFunctionType(llvm::Type* ret_type, uint32_t method_idx, bool is_static); + + llvm::BasicBlock* GetBasicBlock(uint32_t dex_pc); + + llvm::BasicBlock* CreateBasicBlockWithDexPC(uint32_t dex_pc, + const char* postfix); + + int32_t GetTryItemOffset(uint32_t dex_pc); + + llvm::BasicBlock* GetLandingPadBasicBlock(uint32_t dex_pc); + + llvm::BasicBlock* GetUnwindBasicBlock(); + + void EmitGuard_ExceptionLandingPad(uint32_t dex_pc); + + void EmitBranchExceptionLandingPad(uint32_t dex_pc); + + //---------------------------------------------------------------------------- + // Expand Arithmetic Helper Intrinsics + //---------------------------------------------------------------------------- + + llvm::Value* Expand_IntegerShift(llvm::Value* src1_value, + llvm::Value* src2_value, + IntegerShiftKind kind, + JType op_jty); + + public: + static char ID; + + GBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb, + art::CompilerDriver* driver, const art::DexCompilationUnit* dex_compilation_unit) + : llvm::FunctionPass(ID), intrinsic_helper_(intrinsic_helper), irb_(irb), + context_(irb.getContext()), rtb_(irb.Runtime()), + shadow_frame_(NULL), old_shadow_frame_(NULL), + driver_(driver), + dex_compilation_unit_(dex_compilation_unit), + func_(NULL), current_bb_(NULL), basic_block_unwind_(NULL), changed_(false) {} + + bool runOnFunction(llvm::Function& func); + + private: + void InsertStackOverflowCheck(llvm::Function& func); + + llvm::Value* ExpandIntrinsic(IntrinsicHelper::IntrinsicId intr_id, + llvm::CallInst& call_inst); + +}; + +char GBCExpanderPass::ID = 0; + +bool GBCExpanderPass::runOnFunction(llvm::Function& func) { + VLOG(compiler) << "GBC expansion on " << func.getName().str(); + + // Runtime support or stub + if (dex_compilation_unit_ == NULL) { + return false; + } + + // Setup rewrite context + shadow_frame_ = NULL; + old_shadow_frame_ = NULL; + func_ = &func; + changed_ = false; // Assume unchanged + + shadow_frame_vreg_addresses_.resize(dex_compilation_unit_->GetCodeItem()->registers_size_, NULL); + basic_blocks_.resize(dex_compilation_unit_->GetCodeItem()->insns_size_in_code_units_); + basic_block_landing_pads_.resize(dex_compilation_unit_->GetCodeItem()->tries_size_, NULL); + basic_block_unwind_ = NULL; + for (llvm::Function::iterator bb_iter = func_->begin(), bb_end = func_->end(); + bb_iter != bb_end; + ++bb_iter) { + if (bb_iter->begin()->getMetadata("DexOff") == NULL) { + continue; + } + uint32_t dex_pc = LV2UInt(bb_iter->begin()->getMetadata("DexOff")->getOperand(0)); + basic_blocks_[dex_pc] = bb_iter; + } + + // Insert stack overflow check + InsertStackOverflowCheck(func); // TODO: Use intrinsic. + + // Rewrite the intrinsics + RewriteFunction(); + + VERIFY_LLVM_FUNCTION(func); + + return changed_; +} + +void GBCExpanderPass::RewriteBasicBlock(llvm::BasicBlock* original_block) { + llvm::BasicBlock* curr_basic_block = original_block; + + llvm::BasicBlock::iterator inst_iter = original_block->begin(); + llvm::BasicBlock::iterator inst_end = original_block->end(); + + while (inst_iter != inst_end) { + llvm::CallInst* call_inst = llvm::dyn_cast<llvm::CallInst>(inst_iter); + IntrinsicHelper::IntrinsicId intr_id = IntrinsicHelper::UnknownId; + + if (call_inst) { + llvm::Function* callee_func = call_inst->getCalledFunction(); + intr_id = intrinsic_helper_.GetIntrinsicId(callee_func); + } + + if (intr_id == IntrinsicHelper::UnknownId) { + // This is not intrinsic call. Skip this instruction. + ++inst_iter; + continue; + } + + // Rewrite the intrinsic and change the function + changed_ = true; + irb_.SetInsertPoint(inst_iter); + + // Expand the intrinsic + if (llvm::Value* new_value = ExpandIntrinsic(intr_id, *call_inst)) { + inst_iter->replaceAllUsesWith(new_value); + } + + // Remove the old intrinsic call instruction + llvm::BasicBlock::iterator old_inst = inst_iter++; + old_inst->eraseFromParent(); + + // Splice the instruction to the new basic block + llvm::BasicBlock* next_basic_block = irb_.GetInsertBlock(); + if (next_basic_block != curr_basic_block) { + next_basic_block->getInstList().splice( + irb_.GetInsertPoint(), curr_basic_block->getInstList(), + inst_iter, inst_end); + curr_basic_block = next_basic_block; + inst_end = curr_basic_block->end(); + } + } +} + + +void GBCExpanderPass::RewriteFunction() { + size_t num_basic_blocks = func_->getBasicBlockList().size(); + // NOTE: We are not using (bb_iter != bb_end) as the for-loop condition, + // because we will create new basic block while expanding the intrinsics. + // We only want to iterate through the input basic blocks. + + landing_pad_phi_mapping_.clear(); + + for (llvm::Function::iterator bb_iter = func_->begin(); + num_basic_blocks > 0; ++bb_iter, --num_basic_blocks) { + // Set insert point to current basic block. + irb_.SetInsertPoint(bb_iter); + + current_bb_ = bb_iter; + + // Rewrite the basic block + RewriteBasicBlock(bb_iter); + + // Update the phi-instructions in the successor basic block + llvm::BasicBlock* last_block = irb_.GetInsertBlock(); + if (last_block != bb_iter) { + UpdatePhiInstruction(bb_iter, last_block); + } + } + + typedef std::map<llvm::PHINode*, llvm::PHINode*> HandlerPHIMap; + HandlerPHIMap handler_phi; + // Iterate every used landing pad basic block + for (size_t i = 0, ei = basic_block_landing_pads_.size(); i != ei; ++i) { + llvm::BasicBlock* lbb = basic_block_landing_pads_[i]; + if (lbb == NULL) { + continue; + } + + llvm::TerminatorInst* term_inst = lbb->getTerminator(); + std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock*> >& rewrite_pair + = landing_pad_phi_mapping_[lbb]; + irb_.SetInsertPoint(lbb->begin()); + + // Iterate every succeeding basic block (catch block) + for (unsigned succ_iter = 0, succ_end = term_inst->getNumSuccessors(); + succ_iter != succ_end; ++succ_iter) { + llvm::BasicBlock* succ_basic_block = term_inst->getSuccessor(succ_iter); + + // Iterate every phi instructions in the succeeding basic block + for (llvm::BasicBlock::iterator + inst_iter = succ_basic_block->begin(), + inst_end = succ_basic_block->end(); + inst_iter != inst_end; ++inst_iter) { + llvm::PHINode *phi = llvm::dyn_cast<llvm::PHINode>(inst_iter); + + if (!phi) { + break; // Meet non-phi instruction. Done. + } + + if (handler_phi[phi] == NULL) { + handler_phi[phi] = llvm::PHINode::Create(phi->getType(), 1); + } + + // Create new_phi in landing pad + llvm::PHINode* new_phi = irb_.CreatePHI(phi->getType(), rewrite_pair.size()); + // Insert all incoming value into new_phi by rewrite_pair + for (size_t j = 0, ej = rewrite_pair.size(); j != ej; ++j) { + llvm::BasicBlock* old_bb = rewrite_pair[j].first; + llvm::BasicBlock* new_bb = rewrite_pair[j].second; + new_phi->addIncoming(phi->getIncomingValueForBlock(old_bb), new_bb); + } + // Delete all incoming value from phi by rewrite_pair + for (size_t j = 0, ej = rewrite_pair.size(); j != ej; ++j) { + llvm::BasicBlock* old_bb = rewrite_pair[j].first; + int old_bb_idx = phi->getBasicBlockIndex(old_bb); + if (old_bb_idx >= 0) { + phi->removeIncomingValue(old_bb_idx, false); + } + } + // Insert new_phi into new handler phi + handler_phi[phi]->addIncoming(new_phi, lbb); + } + } + } + + // Replace all handler phi + // We can't just use the old handler phi, because some exception edges will disappear after we + // compute fast-path. + for (HandlerPHIMap::iterator it = handler_phi.begin(); it != handler_phi.end(); ++it) { + llvm::PHINode* old_phi = it->first; + llvm::PHINode* new_phi = it->second; + new_phi->insertBefore(old_phi); + old_phi->replaceAllUsesWith(new_phi); + old_phi->eraseFromParent(); + } +} + +void GBCExpanderPass::UpdatePhiInstruction(llvm::BasicBlock* old_basic_block, + llvm::BasicBlock* new_basic_block) { + llvm::TerminatorInst* term_inst = new_basic_block->getTerminator(); + + if (!term_inst) { + return; // No terminating instruction in new_basic_block. Nothing to do. + } + + // Iterate every succeeding basic block + for (unsigned succ_iter = 0, succ_end = term_inst->getNumSuccessors(); + succ_iter != succ_end; ++succ_iter) { + llvm::BasicBlock* succ_basic_block = term_inst->getSuccessor(succ_iter); + + // Iterate every phi instructions in the succeeding basic block + for (llvm::BasicBlock::iterator + inst_iter = succ_basic_block->begin(), + inst_end = succ_basic_block->end(); + inst_iter != inst_end; ++inst_iter) { + llvm::PHINode *phi = llvm::dyn_cast<llvm::PHINode>(inst_iter); + + if (!phi) { + break; // Meet non-phi instruction. Done. + } + + // Update the incoming block of this phi instruction + for (llvm::PHINode::block_iterator + ibb_iter = phi->block_begin(), ibb_end = phi->block_end(); + ibb_iter != ibb_end; ++ibb_iter) { + if (*ibb_iter == old_basic_block) { + *ibb_iter = new_basic_block; + } + } + } + } +} + +llvm::Value* GBCExpanderPass::ExpandToRuntime(runtime_support::RuntimeId rt, + llvm::CallInst& inst) { + // Some GBC intrinsic can directly replace with IBC runtime. "Directly" means + // the arguments passed to the GBC intrinsic are as the same as IBC runtime + // function, therefore only called function is needed to change. + unsigned num_args = inst.getNumArgOperands(); + + if (num_args <= 0) { + return irb_.CreateCall(irb_.GetRuntime(rt)); + } else { + std::vector<llvm::Value*> args; + for (unsigned i = 0; i < num_args; i++) { + args.push_back(inst.getArgOperand(i)); + } + + return irb_.CreateCall(irb_.GetRuntime(rt), args); + } +} + +void +GBCExpanderPass::EmitStackOverflowCheck(llvm::Instruction* first_non_alloca) { + llvm::Function* func = first_non_alloca->getParent()->getParent(); + llvm::Module* module = func->getParent(); + + // Call llvm intrinsic function to get frame address. + llvm::Function* frameaddress = + llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::frameaddress); + + // The type of llvm::frameaddress is: i8* @llvm.frameaddress(i32) + llvm::Value* frame_address = irb_.CreateCall(frameaddress, irb_.getInt32(0)); + + // Cast i8* to int + frame_address = irb_.CreatePtrToInt(frame_address, irb_.getPtrEquivIntTy()); + + // Get thread.stack_end_ + llvm::Value* stack_end = + irb_.Runtime().EmitLoadFromThreadOffset(art::Thread::StackEndOffset().Int32Value(), + irb_.getPtrEquivIntTy(), + kTBAARuntimeInfo); + + // Check the frame address < thread.stack_end_ ? + llvm::Value* is_stack_overflow = irb_.CreateICmpULT(frame_address, stack_end); + + llvm::BasicBlock* block_exception = + llvm::BasicBlock::Create(context_, "stack_overflow", func); + + llvm::BasicBlock* block_continue = + llvm::BasicBlock::Create(context_, "stack_overflow_cont", func); + + irb_.CreateCondBr(is_stack_overflow, block_exception, block_continue, kUnlikely); + + // If stack overflow, throw exception. + irb_.SetInsertPoint(block_exception); + irb_.CreateCall(irb_.GetRuntime(runtime_support::ThrowStackOverflowException)); + + // Unwind. + llvm::Type* ret_type = func->getReturnType(); + if (ret_type->isVoidTy()) { + irb_.CreateRetVoid(); + } else { + // The return value is ignored when there's an exception. MethodCompiler + // returns zero value under the the corresponding return type in this case. + // GBCExpander returns LLVM undef value here for brevity + irb_.CreateRet(llvm::UndefValue::get(ret_type)); + } + + irb_.SetInsertPoint(block_continue); +} + +llvm::Value* GBCExpanderPass::EmitLoadDexCacheAddr(art::MemberOffset offset) { + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + return irb_.LoadFromObjectOffset(method_object_addr, + offset.Int32Value(), + irb_.getJObjectTy(), + kTBAAConstJObject); +} + +llvm::Value* +GBCExpanderPass::EmitLoadDexCacheStaticStorageFieldAddr(uint32_t type_idx) { + llvm::Value* static_storage_dex_cache_addr = + EmitLoadDexCacheAddr(art::mirror::AbstractMethod::DexCacheInitializedStaticStorageOffset()); + + llvm::Value* type_idx_value = irb_.getPtrEquivInt(type_idx); + + return EmitArrayGEP(static_storage_dex_cache_addr, type_idx_value, kObject); +} + +llvm::Value* +GBCExpanderPass::EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx) { + llvm::Value* resolved_type_dex_cache_addr = + EmitLoadDexCacheAddr(art::mirror::AbstractMethod::DexCacheResolvedTypesOffset()); + + llvm::Value* type_idx_value = irb_.getPtrEquivInt(type_idx); + + return EmitArrayGEP(resolved_type_dex_cache_addr, type_idx_value, kObject); +} + +llvm::Value* GBCExpanderPass:: +EmitLoadDexCacheResolvedMethodFieldAddr(uint32_t method_idx) { + llvm::Value* resolved_method_dex_cache_addr = + EmitLoadDexCacheAddr(art::mirror::AbstractMethod::DexCacheResolvedMethodsOffset()); + + llvm::Value* method_idx_value = irb_.getPtrEquivInt(method_idx); + + return EmitArrayGEP(resolved_method_dex_cache_addr, method_idx_value, kObject); +} + +llvm::Value* GBCExpanderPass:: +EmitLoadDexCacheStringFieldAddr(uint32_t string_idx) { + llvm::Value* string_dex_cache_addr = + EmitLoadDexCacheAddr(art::mirror::AbstractMethod::DexCacheStringsOffset()); + + llvm::Value* string_idx_value = irb_.getPtrEquivInt(string_idx); + + return EmitArrayGEP(string_dex_cache_addr, string_idx_value, kObject); +} + +llvm::Value* GBCExpanderPass::EmitLoadMethodObjectAddr() { + llvm::Function* parent_func = irb_.GetInsertBlock()->getParent(); + return parent_func->arg_begin(); +} + +llvm::Value* GBCExpanderPass::EmitLoadArrayLength(llvm::Value* array) { + // Load array length + return irb_.LoadFromObjectOffset(array, + art::mirror::Array::LengthOffset().Int32Value(), + irb_.getJIntTy(), + kTBAAConstJObject); + +} + +llvm::Value* +GBCExpanderPass::EmitLoadSDCalleeMethodObjectAddr(uint32_t callee_method_idx) { + llvm::Value* callee_method_object_field_addr = + EmitLoadDexCacheResolvedMethodFieldAddr(callee_method_idx); + + return irb_.CreateLoad(callee_method_object_field_addr, kTBAARuntimeInfo); +} + +llvm::Value* GBCExpanderPass:: +EmitLoadVirtualCalleeMethodObjectAddr(int vtable_idx, llvm::Value* this_addr) { + // Load class object of *this* pointer + llvm::Value* class_object_addr = + irb_.LoadFromObjectOffset(this_addr, + art::mirror::Object::ClassOffset().Int32Value(), + irb_.getJObjectTy(), + kTBAAConstJObject); + + // Load vtable address + llvm::Value* vtable_addr = + irb_.LoadFromObjectOffset(class_object_addr, + art::mirror::Class::VTableOffset().Int32Value(), + irb_.getJObjectTy(), + kTBAAConstJObject); + + // Load callee method object + llvm::Value* vtable_idx_value = + irb_.getPtrEquivInt(static_cast<uint64_t>(vtable_idx)); + + llvm::Value* method_field_addr = + EmitArrayGEP(vtable_addr, vtable_idx_value, kObject); + + return irb_.CreateLoad(method_field_addr, kTBAAConstJObject); +} + +// Emit Array GetElementPtr +llvm::Value* GBCExpanderPass::EmitArrayGEP(llvm::Value* array_addr, + llvm::Value* index_value, + JType elem_jty) { + + int data_offset; + if (elem_jty == kLong || elem_jty == kDouble || + (elem_jty == kObject && sizeof(uint64_t) == sizeof(art::mirror::Object*))) { + data_offset = art::mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); + } else { + data_offset = art::mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); + } + + llvm::Constant* data_offset_value = + irb_.getPtrEquivInt(data_offset); + + llvm::Type* elem_type = irb_.getJType(elem_jty); + + llvm::Value* array_data_addr = + irb_.CreatePtrDisp(array_addr, data_offset_value, + elem_type->getPointerTo()); + + return irb_.CreateGEP(array_data_addr, index_value); +} + +llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + art::InvokeType invoke_type = + static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0))); + bool is_static = (invoke_type == art::kStatic); + art::MethodReference target_method(dex_compilation_unit_->GetDexFile(), + LV2UInt(call_inst.getArgOperand(1))); + + // Load *this* actual parameter + llvm::Value* this_addr = (!is_static) ? call_inst.getArgOperand(3) : NULL; + + // Compute invoke related information for compiler decision + int vtable_idx = -1; + uintptr_t direct_code = 0; + uintptr_t direct_method = 0; + bool is_fast_path = driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc, + invoke_type, target_method, + vtable_idx, + direct_code, direct_method, + true); + // Load the method object + llvm::Value* callee_method_object_addr = NULL; + + if (!is_fast_path) { + callee_method_object_addr = + EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index, invoke_type, + this_addr, dex_pc, is_fast_path); + } else { + switch (invoke_type) { + case art::kStatic: + case art::kDirect: + if (direct_method != 0u && + direct_method != static_cast<uintptr_t>(-1)) { + callee_method_object_addr = + irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_method), + irb_.getJObjectTy()); + } else { + callee_method_object_addr = + EmitLoadSDCalleeMethodObjectAddr(target_method.dex_method_index); + } + break; + + case art::kVirtual: + DCHECK(vtable_idx != -1); + callee_method_object_addr = + EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr); + break; + + case art::kSuper: + LOG(FATAL) << "invoke-super should be promoted to invoke-direct in " + "the fast path."; + break; + + case art::kInterface: + callee_method_object_addr = + EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index, + invoke_type, this_addr, + dex_pc, is_fast_path); + break; + } + } + + // Load the actual parameter + std::vector<llvm::Value*> args; + + args.push_back(callee_method_object_addr); // method object for callee + + for (uint32_t i = 3; i < call_inst.getNumArgOperands(); ++i) { + args.push_back(call_inst.getArgOperand(i)); + } + + llvm::Value* code_addr; + llvm::Type* func_type = GetFunctionType(call_inst.getType(), + target_method.dex_method_index, is_static); + if (direct_code != 0u && direct_code != static_cast<uintptr_t>(-1)) { + code_addr = + irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_code), + func_type->getPointerTo()); + } else { + code_addr = + irb_.LoadFromObjectOffset(callee_method_object_addr, + art::mirror::AbstractMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), + func_type->getPointerTo(), kTBAARuntimeInfo); + } + + // Invoke callee + EmitUpdateDexPC(dex_pc); + llvm::Value* retval = irb_.CreateCall(code_addr, args); + EmitGuard_ExceptionLandingPad(dex_pc); + + return retval; +} + +bool GBCExpanderPass::EmitIntrinsic(llvm::CallInst& call_inst, + llvm::Value** result) { + DCHECK(result != NULL); + + uint32_t callee_method_idx = LV2UInt(call_inst.getArgOperand(1)); + std::string callee_method_name( + PrettyMethod(callee_method_idx, *dex_compilation_unit_->GetDexFile())); + + if (callee_method_name == "int java.lang.String.length()") { + return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result, + false /* is_empty */); + } + if (callee_method_name == "boolean java.lang.String.isEmpty()") { + return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result, + true /* is_empty */); + } + + *result = NULL; + return false; +} + +bool GBCExpanderPass::EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst, + llvm::Value** result, + bool is_empty) { + art::InvokeType invoke_type = + static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0))); + DCHECK_NE(invoke_type, art::kStatic); + DCHECK_EQ(call_inst.getNumArgOperands(), 4U); + + llvm::Value* this_object = call_inst.getArgOperand(3); + llvm::Value* string_count = + irb_.LoadFromObjectOffset(this_object, + art::mirror::String::CountOffset().Int32Value(), + irb_.getJIntTy(), + kTBAAConstJObject); + if (is_empty) { + llvm::Value* count_equals_zero = irb_.CreateICmpEQ(string_count, + irb_.getJInt(0)); + llvm::Value* is_empty = irb_.CreateSelect(count_equals_zero, + irb_.getJBoolean(true), + irb_.getJBoolean(false)); + is_empty = SignOrZeroExtendCat1Types(is_empty, kBoolean); + *result = is_empty; + } else { + *result = string_count; + } + return true; +} + +void GBCExpanderPass::Expand_TestSuspend(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + + llvm::Value* suspend_count = + irb_.Runtime().EmitLoadFromThreadOffset(art::Thread::ThreadFlagsOffset().Int32Value(), + irb_.getInt16Ty(), + kTBAARuntimeInfo); + llvm::Value* is_suspend = irb_.CreateICmpNE(suspend_count, irb_.getInt16(0)); + + llvm::BasicBlock* basic_block_suspend = CreateBasicBlockWithDexPC(dex_pc, "suspend"); + llvm::BasicBlock* basic_block_cont = CreateBasicBlockWithDexPC(dex_pc, "suspend_cont"); + + irb_.CreateCondBr(is_suspend, basic_block_suspend, basic_block_cont, kUnlikely); + + irb_.SetInsertPoint(basic_block_suspend); + if (dex_pc != art::DexFile::kDexNoIndex) { + EmitUpdateDexPC(dex_pc); + } + irb_.Runtime().EmitTestSuspend(); + + 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; +} + +void GBCExpanderPass::Expand_MarkGCCard(llvm::CallInst& call_inst) { + irb_.Runtime().EmitMarkGCCard(call_inst.getArgOperand(0), call_inst.getArgOperand(1)); + return; +} + +llvm::Value* +GBCExpanderPass::Expand_LoadStringFromDexCache(llvm::Value* string_idx_value) { + uint32_t string_idx = + llvm::cast<llvm::ConstantInt>(string_idx_value)->getZExtValue(); + + llvm::Value* string_field_addr = EmitLoadDexCacheStringFieldAddr(string_idx); + + return irb_.CreateLoad(string_field_addr, kTBAARuntimeInfo); +} + +llvm::Value* +GBCExpanderPass::Expand_LoadTypeFromDexCache(llvm::Value* type_idx_value) { + uint32_t type_idx = + llvm::cast<llvm::ConstantInt>(type_idx_value)->getZExtValue(); + + llvm::Value* type_field_addr = + EmitLoadDexCacheResolvedTypeFieldAddr(type_idx); + + return irb_.CreateLoad(type_field_addr, kTBAARuntimeInfo); +} + +void GBCExpanderPass::Expand_LockObject(llvm::Value* obj) { + rtb_.EmitLockObject(obj); + return; +} + +void GBCExpanderPass::Expand_UnlockObject(llvm::Value* obj) { + rtb_.EmitUnlockObject(obj); + return; +} + +llvm::Value* GBCExpanderPass::Expand_ArrayGet(llvm::Value* array_addr, + llvm::Value* index_value, + JType elem_jty) { + llvm::Value* array_elem_addr = + EmitArrayGEP(array_addr, index_value, elem_jty); + + return irb_.CreateLoad(array_elem_addr, kTBAAHeapArray, elem_jty); +} + +void GBCExpanderPass::Expand_ArrayPut(llvm::Value* new_value, + llvm::Value* array_addr, + llvm::Value* index_value, + JType elem_jty) { + llvm::Value* array_elem_addr = + EmitArrayGEP(array_addr, index_value, elem_jty); + + irb_.CreateStore(new_value, array_elem_addr, kTBAAHeapArray, elem_jty); + + return; +} + +void GBCExpanderPass::Expand_FilledNewArray(llvm::CallInst& call_inst) { + // Most of the codes refer to MethodCompiler::EmitInsn_FilledNewArray + llvm::Value* array = call_inst.getArgOperand(0); + + uint32_t element_jty = + llvm::cast<llvm::ConstantInt>(call_inst.getArgOperand(1))->getZExtValue(); + + DCHECK(call_inst.getNumArgOperands() > 2); + unsigned num_elements = (call_inst.getNumArgOperands() - 2); + + bool is_elem_int_ty = (static_cast<JType>(element_jty) == kInt); + + uint32_t alignment; + llvm::Constant* elem_size; + llvm::PointerType* field_type; + + // NOTE: Currently filled-new-array only supports 'L', '[', and 'I' + // as the element, thus we are only checking 2 cases: primitive int and + // non-primitive type. + if (is_elem_int_ty) { + alignment = sizeof(int32_t); + elem_size = irb_.getPtrEquivInt(sizeof(int32_t)); + field_type = irb_.getJIntTy()->getPointerTo(); + } else { + alignment = irb_.getSizeOfPtrEquivInt(); + elem_size = irb_.getSizeOfPtrEquivIntValue(); + field_type = irb_.getJObjectTy()->getPointerTo(); + } + + llvm::Value* data_field_offset = + irb_.getPtrEquivInt(art::mirror::Array::DataOffset(alignment).Int32Value()); + + llvm::Value* data_field_addr = + irb_.CreatePtrDisp(array, data_field_offset, field_type); + + for (unsigned i = 0; i < num_elements; ++i) { + // Values to fill the array begin at the 3rd argument + llvm::Value* reg_value = call_inst.getArgOperand(2 + i); + + irb_.CreateStore(reg_value, data_field_addr, kTBAAHeapArray); + + data_field_addr = + irb_.CreatePtrDisp(data_field_addr, elem_size, field_type); + } + + return; +} + +llvm::Value* GBCExpanderPass::Expand_IGetFast(llvm::Value* field_offset_value, + llvm::Value* /*is_volatile_value*/, + llvm::Value* object_addr, + JType field_jty) { + int field_offset = + llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue(); + + DCHECK_GE(field_offset, 0); + + llvm::PointerType* field_type = + irb_.getJType(field_jty)->getPointerTo(); + + field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* field_addr = + irb_.CreatePtrDisp(object_addr, field_offset_value, field_type); + + // TODO: Check is_volatile. We need to generate atomic load instruction + // when is_volatile is true. + return irb_.CreateLoad(field_addr, kTBAAHeapInstance, field_jty); +} + +void GBCExpanderPass::Expand_IPutFast(llvm::Value* field_offset_value, + llvm::Value* /* is_volatile_value */, + llvm::Value* object_addr, + llvm::Value* new_value, + JType field_jty) { + int field_offset = + llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue(); + + DCHECK_GE(field_offset, 0); + + llvm::PointerType* field_type = + irb_.getJType(field_jty)->getPointerTo(); + + field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* field_addr = + irb_.CreatePtrDisp(object_addr, field_offset_value, field_type); + + // TODO: Check is_volatile. We need to generate atomic store instruction + // when is_volatile is true. + irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty); + + return; +} + +llvm::Value* GBCExpanderPass::Expand_SGetFast(llvm::Value* static_storage_addr, + llvm::Value* field_offset_value, + llvm::Value* /*is_volatile_value*/, + JType field_jty) { + int field_offset = + llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue(); + + DCHECK_GE(field_offset, 0); + + llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* static_field_addr = + irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value, + irb_.getJType(field_jty)->getPointerTo()); + + // TODO: Check is_volatile. We need to generate atomic store instruction + // when is_volatile is true. + return irb_.CreateLoad(static_field_addr, kTBAAHeapStatic, field_jty); +} + +void GBCExpanderPass::Expand_SPutFast(llvm::Value* static_storage_addr, + llvm::Value* field_offset_value, + llvm::Value* /* is_volatile_value */, + llvm::Value* new_value, + JType field_jty) { + int field_offset = + llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue(); + + DCHECK_GE(field_offset, 0); + + llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* static_field_addr = + irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value, + irb_.getJType(field_jty)->getPointerTo()); + + // TODO: Check is_volatile. We need to generate atomic store instruction + // when is_volatile is true. + irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty); + + return; +} + +llvm::Value* +GBCExpanderPass::Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr) { + return irb_.LoadFromObjectOffset(method_object_addr, + art::mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), + irb_.getJObjectTy(), + kTBAAConstJObject); +} + +llvm::Value* +GBCExpanderPass::Expand_LoadClassSSBFromDexCache(llvm::Value* type_idx_value) { + uint32_t type_idx = + llvm::cast<llvm::ConstantInt>(type_idx_value)->getZExtValue(); + + llvm::Value* storage_field_addr = + EmitLoadDexCacheStaticStorageFieldAddr(type_idx); + + return irb_.CreateLoad(storage_field_addr, kTBAARuntimeInfo); +} + +llvm::Value* +GBCExpanderPass::Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value) { + uint32_t callee_method_idx = + llvm::cast<llvm::ConstantInt>(callee_method_idx_value)->getZExtValue(); + + return EmitLoadSDCalleeMethodObjectAddr(callee_method_idx); +} + +llvm::Value* GBCExpanderPass::Expand_GetVirtualCalleeMethodObjAddrFast( + llvm::Value* vtable_idx_value, + llvm::Value* this_addr) { + int vtable_idx = + llvm::cast<llvm::ConstantInt>(vtable_idx_value)->getSExtValue(); + + return EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr); +} + +llvm::Value* GBCExpanderPass::Expand_Invoke(llvm::CallInst& call_inst) { + // Most of the codes refer to MethodCompiler::EmitInsn_Invoke + llvm::Value* callee_method_object_addr = call_inst.getArgOperand(0); + unsigned num_args = call_inst.getNumArgOperands(); + llvm::Type* ret_type = call_inst.getType(); + + // Determine the function type of the callee method + std::vector<llvm::Type*> args_type; + std::vector<llvm::Value*> args; + for (unsigned i = 0; i < num_args; i++) { + args.push_back(call_inst.getArgOperand(i)); + args_type.push_back(args[i]->getType()); + } + + llvm::FunctionType* callee_method_type = + llvm::FunctionType::get(ret_type, args_type, false); + + llvm::Value* code_addr = + irb_.LoadFromObjectOffset(callee_method_object_addr, + art::mirror::AbstractMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), + callee_method_type->getPointerTo(), + kTBAARuntimeInfo); + + // Invoke callee + llvm::Value* retval = irb_.CreateCall(code_addr, args); + + return retval; +} + +llvm::Value* GBCExpanderPass::Expand_DivRem(llvm::CallInst& call_inst, + bool is_div, JType op_jty) { + llvm::Value* dividend = call_inst.getArgOperand(0); + llvm::Value* divisor = call_inst.getArgOperand(1); + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + EmitGuard_DivZeroException(dex_pc, divisor, op_jty); + // Most of the codes refer to MethodCompiler::EmitIntDivRemResultComputation + + // Check the special case: MININT / -1 = MININT + // That case will cause overflow, which is undefined behavior in llvm. + // So we check the divisor is -1 or not, if the divisor is -1, we do + // the special path to avoid undefined behavior. + llvm::Type* op_type = irb_.getJType(op_jty); + llvm::Value* zero = irb_.getJZero(op_jty); + llvm::Value* neg_one = llvm::ConstantInt::getSigned(op_type, -1); + + llvm::Function* parent = irb_.GetInsertBlock()->getParent(); + llvm::BasicBlock* eq_neg_one = llvm::BasicBlock::Create(context_, "", parent); + llvm::BasicBlock* ne_neg_one = llvm::BasicBlock::Create(context_, "", parent); + llvm::BasicBlock* neg_one_cont = + llvm::BasicBlock::Create(context_, "", parent); + + llvm::Value* is_equal_neg_one = irb_.CreateICmpEQ(divisor, neg_one); + irb_.CreateCondBr(is_equal_neg_one, eq_neg_one, ne_neg_one, kUnlikely); + + // If divisor == -1 + irb_.SetInsertPoint(eq_neg_one); + llvm::Value* eq_result; + if (is_div) { + // We can just change from "dividend div -1" to "neg dividend". The sub + // don't care the sign/unsigned because of two's complement representation. + // And the behavior is what we want: + // -(2^n) (2^n)-1 + // MININT < k <= MAXINT -> mul k -1 = -k + // MININT == k -> mul k -1 = k + // + // LLVM use sub to represent 'neg' + eq_result = irb_.CreateSub(zero, dividend); + } else { + // Everything modulo -1 will be 0. + eq_result = zero; + } + irb_.CreateBr(neg_one_cont); + + // If divisor != -1, just do the division. + irb_.SetInsertPoint(ne_neg_one); + llvm::Value* ne_result; + if (is_div) { + ne_result = irb_.CreateSDiv(dividend, divisor); + } else { + ne_result = irb_.CreateSRem(dividend, divisor); + } + irb_.CreateBr(neg_one_cont); + + irb_.SetInsertPoint(neg_one_cont); + llvm::PHINode* result = irb_.CreatePHI(op_type, 2); + result->addIncoming(eq_result, eq_neg_one); + result->addIncoming(ne_result, ne_neg_one); + + return result; +} + +void GBCExpanderPass::Expand_AllocaShadowFrame(llvm::Value* num_vregs_value) { + // Most of the codes refer to MethodCompiler::EmitPrologueAllocShadowFrame and + // MethodCompiler::EmitPushShadowFrame + uint16_t num_vregs = + llvm::cast<llvm::ConstantInt>(num_vregs_value)->getZExtValue(); + + llvm::StructType* shadow_frame_type = + irb_.getShadowFrameTy(num_vregs); + + // Create allocas at the start of entry block. + llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP(); + llvm::BasicBlock* entry_block = &func_->front(); + irb_.SetInsertPoint(&entry_block->front()); + + 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_.restoreIP(irb_ip_original); + + // Push the shadow frame + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* shadow_frame_upcast = + irb_.CreateConstGEP2_32(shadow_frame_, 0, 0); + + llvm::Value* result = rtb_.EmitPushShadowFrame(shadow_frame_upcast, + method_object_addr, + num_vregs); + + irb_.CreateStore(result, old_shadow_frame_, kTBAARegister); + + return; +} + +void GBCExpanderPass::Expand_SetVReg(llvm::Value* entry_idx, + llvm::Value* value) { + unsigned vreg_idx = LV2UInt(entry_idx); + DCHECK_LT(vreg_idx, dex_compilation_unit_->GetCodeItem()->registers_size_); + + llvm::Value* vreg_addr = shadow_frame_vreg_addresses_[vreg_idx]; + if (UNLIKELY(vreg_addr == NULL)) { + DCHECK(shadow_frame_ != NULL); + + llvm::Value* gep_index[] = { + irb_.getInt32(0), // No pointer displacement + irb_.getInt32(1), // VRegs + entry_idx // Pointer field + }; + + // A shadow frame address must dominate every use in the function so we + // place it in the entry block right after the allocas. + llvm::BasicBlock::iterator first_non_alloca = func_->getEntryBlock().begin(); + while (llvm::isa<llvm::AllocaInst>(first_non_alloca)) { + ++first_non_alloca; + } + + llvm::IRBuilderBase::InsertPoint ip = irb_.saveIP(); + irb_.SetInsertPoint(static_cast<llvm::Instruction*>(first_non_alloca)); + vreg_addr = irb_.CreateGEP(shadow_frame_, gep_index); + shadow_frame_vreg_addresses_[vreg_idx] = vreg_addr; + irb_.restoreIP(ip); + } + + irb_.CreateStore(value, + irb_.CreateBitCast(vreg_addr, value->getType()->getPointerTo()), + kTBAAShadowFrame); + return; +} + +void GBCExpanderPass::Expand_PopShadowFrame() { + if (old_shadow_frame_ == NULL) { + return; + } + rtb_.EmitPopShadowFrame(irb_.CreateLoad(old_shadow_frame_, kTBAARegister)); + return; +} + +void GBCExpanderPass::Expand_UpdateDexPC(llvm::Value* dex_pc_value) { + irb_.StoreToObjectOffset(shadow_frame_, + art::ShadowFrame::DexPCOffset(), + dex_pc_value, + kTBAAShadowFrame); + return; +} + +void GBCExpanderPass::InsertStackOverflowCheck(llvm::Function& func) { + // All alloca instructions are generated in the first basic block of the + // function, and there are no alloca instructions after the first non-alloca + // instruction. + + llvm::BasicBlock* first_basic_block = &func.front(); + + // Look for first non-alloca instruction + llvm::BasicBlock::iterator first_non_alloca = first_basic_block->begin(); + while (llvm::isa<llvm::AllocaInst>(first_non_alloca)) { + ++first_non_alloca; + } + + irb_.SetInsertPoint(first_non_alloca); + + // Insert stack overflow check codes before first_non_alloca (i.e., after all + // alloca instructions) + EmitStackOverflowCheck(&*first_non_alloca); + + irb_.Runtime().EmitTestSuspend(); + + llvm::BasicBlock* next_basic_block = irb_.GetInsertBlock(); + if (next_basic_block != first_basic_block) { + // Splice the rest of the instruction to the continuing basic block + next_basic_block->getInstList().splice( + irb_.GetInsertPoint(), first_basic_block->getInstList(), + first_non_alloca, first_basic_block->end()); + + // Rewrite the basic block + RewriteBasicBlock(next_basic_block); + + // Update the phi-instructions in the successor basic block + UpdatePhiInstruction(first_basic_block, irb_.GetInsertBlock()); + } + + // We have changed the basic block + changed_ = true; +} + +// ==== High-level intrinsic expander ========================================== + +llvm::Value* GBCExpanderPass::Expand_FPCompare(llvm::Value* src1_value, + llvm::Value* src2_value, + bool gt_bias) { + llvm::Value* cmp_eq = irb_.CreateFCmpOEQ(src1_value, src2_value); + llvm::Value* cmp_lt; + + if (gt_bias) { + cmp_lt = irb_.CreateFCmpOLT(src1_value, src2_value); + } else { + cmp_lt = irb_.CreateFCmpULT(src1_value, src2_value); + } + + return EmitCompareResultSelection(cmp_eq, cmp_lt); +} + +llvm::Value* GBCExpanderPass::Expand_LongCompare(llvm::Value* src1_value, llvm::Value* src2_value) { + llvm::Value* cmp_eq = irb_.CreateICmpEQ(src1_value, src2_value); + llvm::Value* cmp_lt = irb_.CreateICmpSLT(src1_value, src2_value); + + return EmitCompareResultSelection(cmp_eq, cmp_lt); +} + +llvm::Value* GBCExpanderPass::EmitCompareResultSelection(llvm::Value* cmp_eq, + llvm::Value* cmp_lt) { + + llvm::Constant* zero = irb_.getJInt(0); + llvm::Constant* pos1 = irb_.getJInt(1); + llvm::Constant* neg1 = irb_.getJInt(-1); + + llvm::Value* result_lt = irb_.CreateSelect(cmp_lt, neg1, pos1); + llvm::Value* result_eq = irb_.CreateSelect(cmp_eq, zero, result_lt); + + return result_eq; +} + +llvm::Value* GBCExpanderPass::Expand_IntegerShift(llvm::Value* src1_value, + llvm::Value* src2_value, + IntegerShiftKind kind, + JType op_jty) { + DCHECK(op_jty == kInt || op_jty == kLong); + + // Mask and zero-extend RHS properly + if (op_jty == kInt) { + src2_value = irb_.CreateAnd(src2_value, 0x1f); + } else { + llvm::Value* masked_src2_value = irb_.CreateAnd(src2_value, 0x3f); + src2_value = irb_.CreateZExt(masked_src2_value, irb_.getJLongTy()); + } + + // Create integer shift llvm instruction + switch (kind) { + case kIntegerSHL: + return irb_.CreateShl(src1_value, src2_value); + + case kIntegerSHR: + return irb_.CreateAShr(src1_value, src2_value); + + case kIntegerUSHR: + return irb_.CreateLShr(src1_value, src2_value); + + default: + LOG(FATAL) << "Unknown integer shift kind: " << kind; + return NULL; + } +} + +llvm::Value* GBCExpanderPass::SignOrZeroExtendCat1Types(llvm::Value* value, JType jty) { + switch (jty) { + case kBoolean: + case kChar: + return irb_.CreateZExt(value, irb_.getJType(kInt)); + case kByte: + case kShort: + return irb_.CreateSExt(value, irb_.getJType(kInt)); + case kVoid: + case kInt: + case kLong: + case kFloat: + case kDouble: + case kObject: + return value; // Nothing to do. + default: + LOG(FATAL) << "Unknown java type: " << jty; + return NULL; + } +} + +llvm::Value* GBCExpanderPass::TruncateCat1Types(llvm::Value* value, JType jty) { + switch (jty) { + case kBoolean: + case kChar: + case kByte: + case kShort: + return irb_.CreateTrunc(value, irb_.getJType(jty)); + case kVoid: + case kInt: + case kLong: + case kFloat: + case kDouble: + case kObject: + return value; // Nothing to do. + default: + LOG(FATAL) << "Unknown java type: " << jty; + return NULL; + } +} + +llvm::Value* GBCExpanderPass::Expand_HLArrayGet(llvm::CallInst& call_inst, + JType elem_jty) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + llvm::Value* array_addr = call_inst.getArgOperand(1); + llvm::Value* index_value = call_inst.getArgOperand(2); + int opt_flags = LV2UInt(call_inst.getArgOperand(0)); + + EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags); + EmitGuard_ArrayIndexOutOfBoundsException(dex_pc, array_addr, index_value, + opt_flags); + + llvm::Value* array_elem_addr = EmitArrayGEP(array_addr, index_value, elem_jty); + + llvm::Value* array_elem_value = irb_.CreateLoad(array_elem_addr, kTBAAHeapArray, elem_jty); + + return SignOrZeroExtendCat1Types(array_elem_value, elem_jty); +} + + +void GBCExpanderPass::Expand_HLArrayPut(llvm::CallInst& call_inst, + JType elem_jty) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + llvm::Value* new_value = call_inst.getArgOperand(1); + llvm::Value* array_addr = call_inst.getArgOperand(2); + llvm::Value* index_value = call_inst.getArgOperand(3); + int opt_flags = LV2UInt(call_inst.getArgOperand(0)); + + EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags); + EmitGuard_ArrayIndexOutOfBoundsException(dex_pc, array_addr, index_value, + opt_flags); + + new_value = TruncateCat1Types(new_value, elem_jty); + + llvm::Value* array_elem_addr = EmitArrayGEP(array_addr, index_value, elem_jty); + + if (elem_jty == kObject) { // If put an object, check the type, and mark GC card table. + llvm::Function* runtime_func = irb_.GetRuntime(runtime_support::CheckPutArrayElement); + + irb_.CreateCall2(runtime_func, new_value, array_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + EmitMarkGCCard(new_value, array_addr); + } + + irb_.CreateStore(new_value, array_elem_addr, kTBAAHeapArray, elem_jty); + + return; +} + +llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst, + JType field_jty) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + llvm::Value* object_addr = call_inst.getArgOperand(1); + uint32_t field_idx = LV2UInt(call_inst.getArgOperand(2)); + int opt_flags = LV2UInt(call_inst.getArgOperand(0)); + + EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags); + + llvm::Value* field_value; + + int field_offset; + bool is_volatile; + bool is_fast_path = driver_->ComputeInstanceFieldInfo( + field_idx, dex_compilation_unit_, field_offset, is_volatile, false); + + if (!is_fast_path) { + llvm::Function* runtime_func; + + if (field_jty == kObject) { + runtime_func = irb_.GetRuntime(runtime_support::GetObjectInstance); + } else if (field_jty == kLong || field_jty == kDouble) { + runtime_func = irb_.GetRuntime(runtime_support::Get64Instance); + } else { + runtime_func = irb_.GetRuntime(runtime_support::Get32Instance); + } + + llvm::ConstantInt* field_idx_value = irb_.getInt32(field_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + EmitUpdateDexPC(dex_pc); + + field_value = irb_.CreateCall3(runtime_func, field_idx_value, + method_object_addr, object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + if (field_jty == kFloat || field_jty == kDouble) { + field_value = irb_.CreateBitCast(field_value, irb_.getJType(field_jty)); + } + } else { + DCHECK_GE(field_offset, 0); + + llvm::PointerType* field_type = + irb_.getJType(field_jty)->getPointerTo(); + + llvm::ConstantInt* field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* field_addr = + irb_.CreatePtrDisp(object_addr, field_offset_value, field_type); + + field_value = irb_.CreateLoad(field_addr, kTBAAHeapInstance, field_jty); + field_value = SignOrZeroExtendCat1Types(field_value, field_jty); + + if (is_volatile) { + irb_.CreateMemoryBarrier(art::kLoadLoad); + } + } + + return field_value; +} + +void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst, + JType field_jty) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + llvm::Value* new_value = call_inst.getArgOperand(1); + llvm::Value* object_addr = call_inst.getArgOperand(2); + uint32_t field_idx = LV2UInt(call_inst.getArgOperand(3)); + int opt_flags = LV2UInt(call_inst.getArgOperand(0)); + + EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags); + + int field_offset; + bool is_volatile; + bool is_fast_path = driver_->ComputeInstanceFieldInfo( + field_idx, dex_compilation_unit_, field_offset, is_volatile, true); + + if (!is_fast_path) { + llvm::Function* runtime_func; + + if (field_jty == kFloat) { + new_value = irb_.CreateBitCast(new_value, irb_.getJType(kInt)); + } else if (field_jty == kDouble) { + new_value = irb_.CreateBitCast(new_value, irb_.getJType(kLong)); + } + + if (field_jty == kObject) { + runtime_func = irb_.GetRuntime(runtime_support::SetObjectInstance); + } else if (field_jty == kLong || field_jty == kDouble) { + runtime_func = irb_.GetRuntime(runtime_support::Set64Instance); + } else { + runtime_func = irb_.GetRuntime(runtime_support::Set32Instance); + } + + llvm::Value* field_idx_value = irb_.getInt32(field_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + EmitUpdateDexPC(dex_pc); + + irb_.CreateCall4(runtime_func, field_idx_value, + method_object_addr, object_addr, new_value); + + EmitGuard_ExceptionLandingPad(dex_pc); + + } else { + DCHECK_GE(field_offset, 0); + + if (is_volatile) { + irb_.CreateMemoryBarrier(art::kStoreStore); + } + + llvm::PointerType* field_type = + irb_.getJType(field_jty)->getPointerTo(); + + llvm::Value* field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* field_addr = + irb_.CreatePtrDisp(object_addr, field_offset_value, field_type); + + new_value = TruncateCat1Types(new_value, field_jty); + irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty); + + if (is_volatile) { + irb_.CreateMemoryBarrier(art::kLoadLoad); + } + + if (field_jty == kObject) { // If put an object, mark the GC card table. + EmitMarkGCCard(new_value, object_addr); + } + } + + return; +} + +llvm::Value* GBCExpanderPass::EmitLoadConstantClass(uint32_t dex_pc, + uint32_t type_idx) { + if (!driver_->CanAccessTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(), + *dex_compilation_unit_->GetDexFile(), type_idx)) { + llvm::Value* type_idx_value = irb_.getInt32(type_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread(); + + llvm::Function* runtime_func = + irb_.GetRuntime(runtime_support::InitializeTypeAndVerifyAccess); + + EmitUpdateDexPC(dex_pc); + + llvm::Value* type_object_addr = + irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + return type_object_addr; + + } else { + // Try to load the class (type) object from the test cache. + llvm::Value* type_field_addr = + EmitLoadDexCacheResolvedTypeFieldAddr(type_idx); + + llvm::Value* type_object_addr = irb_.CreateLoad(type_field_addr, kTBAARuntimeInfo); + + if (driver_->CanAssumeTypeIsPresentInDexCache(*dex_compilation_unit_->GetDexFile(), type_idx)) { + return type_object_addr; + } + + llvm::BasicBlock* block_original = irb_.GetInsertBlock(); + + // Test whether class (type) object is in the dex cache or not + llvm::Value* equal_null = + irb_.CreateICmpEQ(type_object_addr, irb_.getJNull()); + + llvm::BasicBlock* block_cont = + CreateBasicBlockWithDexPC(dex_pc, "cont"); + + llvm::BasicBlock* block_load_class = + CreateBasicBlockWithDexPC(dex_pc, "load_class"); + + irb_.CreateCondBr(equal_null, block_load_class, block_cont, kUnlikely); + + // Failback routine to load the class object + irb_.SetInsertPoint(block_load_class); + + llvm::Function* runtime_func = irb_.GetRuntime(runtime_support::InitializeType); + + llvm::Constant* type_idx_value = irb_.getInt32(type_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread(); + + EmitUpdateDexPC(dex_pc); + + llvm::Value* loaded_type_object_addr = + irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + llvm::BasicBlock* block_after_load_class = irb_.GetInsertBlock(); + + irb_.CreateBr(block_cont); + + // Now the class object must be loaded + irb_.SetInsertPoint(block_cont); + + llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2); + + phi->addIncoming(type_object_addr, block_original); + phi->addIncoming(loaded_type_object_addr, block_after_load_class); + + return phi; + } +} + +llvm::Value* GBCExpanderPass::EmitLoadStaticStorage(uint32_t dex_pc, + uint32_t type_idx) { + llvm::BasicBlock* block_load_static = + CreateBasicBlockWithDexPC(dex_pc, "load_static"); + + llvm::BasicBlock* block_cont = CreateBasicBlockWithDexPC(dex_pc, "cont"); + + // Load static storage from dex cache + llvm::Value* storage_field_addr = + EmitLoadDexCacheStaticStorageFieldAddr(type_idx); + + llvm::Value* storage_object_addr = irb_.CreateLoad(storage_field_addr, kTBAARuntimeInfo); + + llvm::BasicBlock* block_original = irb_.GetInsertBlock(); + + // Test: Is the static storage of this class initialized? + llvm::Value* equal_null = + irb_.CreateICmpEQ(storage_object_addr, irb_.getJNull()); + + irb_.CreateCondBr(equal_null, block_load_static, block_cont, kUnlikely); + + // Failback routine to load the class object + irb_.SetInsertPoint(block_load_static); + + llvm::Function* runtime_func = irb_.GetRuntime(runtime_support::InitializeStaticStorage); + + llvm::Constant* type_idx_value = irb_.getInt32(type_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread(); + + EmitUpdateDexPC(dex_pc); + + llvm::Value* loaded_storage_object_addr = + irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + llvm::BasicBlock* block_after_load_static = irb_.GetInsertBlock(); + + irb_.CreateBr(block_cont); + + // Now the class object must be loaded + irb_.SetInsertPoint(block_cont); + + llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2); + + phi->addIncoming(storage_object_addr, block_original); + phi->addIncoming(loaded_storage_object_addr, block_after_load_static); + + return phi; +} + +llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst, + JType field_jty) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t field_idx = LV2UInt(call_inst.getArgOperand(0)); + + int field_offset; + int ssb_index; + bool is_referrers_class; + bool is_volatile; + + bool is_fast_path = driver_->ComputeStaticFieldInfo( + field_idx, dex_compilation_unit_, field_offset, ssb_index, + is_referrers_class, is_volatile, false); + + llvm::Value* static_field_value; + + if (!is_fast_path) { + llvm::Function* runtime_func; + + if (field_jty == kObject) { + runtime_func = irb_.GetRuntime(runtime_support::GetObjectStatic); + } else if (field_jty == kLong || field_jty == kDouble) { + runtime_func = irb_.GetRuntime(runtime_support::Get64Static); + } else { + runtime_func = irb_.GetRuntime(runtime_support::Get32Static); + } + + llvm::Constant* field_idx_value = irb_.getInt32(field_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + EmitUpdateDexPC(dex_pc); + + static_field_value = + irb_.CreateCall2(runtime_func, field_idx_value, method_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + if (field_jty == kFloat || field_jty == kDouble) { + static_field_value = irb_.CreateBitCast(static_field_value, irb_.getJType(field_jty)); + } + } else { + DCHECK_GE(field_offset, 0); + + llvm::Value* static_storage_addr = NULL; + + if (is_referrers_class) { + // Fast path, static storage base is this method's class + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + static_storage_addr = + irb_.LoadFromObjectOffset(method_object_addr, + art::mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), + irb_.getJObjectTy(), + kTBAAConstJObject); + } else { + // Medium path, static storage base in a different class which + // requires checks that the other class is initialized + DCHECK_GE(ssb_index, 0); + static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index); + } + + llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* static_field_addr = + irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value, + irb_.getJType(field_jty)->getPointerTo()); + + static_field_value = irb_.CreateLoad(static_field_addr, kTBAAHeapStatic, field_jty); + static_field_value = SignOrZeroExtendCat1Types(static_field_value, field_jty); + + if (is_volatile) { + irb_.CreateMemoryBarrier(art::kLoadLoad); + } + } + + return static_field_value; +} + +void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, + JType field_jty) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t field_idx = LV2UInt(call_inst.getArgOperand(0)); + llvm::Value* new_value = call_inst.getArgOperand(1); + + if (field_jty == kFloat || field_jty == kDouble) { + new_value = irb_.CreateBitCast(new_value, irb_.getJType(field_jty)); + } + + int field_offset; + int ssb_index; + bool is_referrers_class; + bool is_volatile; + + bool is_fast_path = driver_->ComputeStaticFieldInfo( + field_idx, dex_compilation_unit_, field_offset, ssb_index, + is_referrers_class, is_volatile, true); + + if (!is_fast_path) { + llvm::Function* runtime_func; + + if (field_jty == kObject) { + runtime_func = irb_.GetRuntime(runtime_support::SetObjectStatic); + } else if (field_jty == kLong || field_jty == kDouble) { + runtime_func = irb_.GetRuntime(runtime_support::Set64Static); + } else { + runtime_func = irb_.GetRuntime(runtime_support::Set32Static); + } + + if (field_jty == kFloat) { + new_value = irb_.CreateBitCast(new_value, irb_.getJType(kInt)); + } else if (field_jty == kDouble) { + new_value = irb_.CreateBitCast(new_value, irb_.getJType(kLong)); + } + + llvm::Constant* field_idx_value = irb_.getInt32(field_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + EmitUpdateDexPC(dex_pc); + + irb_.CreateCall3(runtime_func, field_idx_value, + method_object_addr, new_value); + + EmitGuard_ExceptionLandingPad(dex_pc); + + } else { + DCHECK_GE(field_offset, 0); + + llvm::Value* static_storage_addr = NULL; + + if (is_referrers_class) { + // Fast path, static storage base is this method's class + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + static_storage_addr = + irb_.LoadFromObjectOffset(method_object_addr, + art::mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), + irb_.getJObjectTy(), + kTBAAConstJObject); + } else { + // Medium path, static storage base in a different class which + // requires checks that the other class is initialized + DCHECK_GE(ssb_index, 0); + static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index); + } + + if (is_volatile) { + irb_.CreateMemoryBarrier(art::kStoreStore); + } + + llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset); + + llvm::Value* static_field_addr = + irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value, + irb_.getJType(field_jty)->getPointerTo()); + + new_value = TruncateCat1Types(new_value, field_jty); + irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty); + + if (is_volatile) { + irb_.CreateMemoryBarrier(art::kStoreLoad); + } + + if (field_jty == kObject) { // If put an object, mark the GC card table. + EmitMarkGCCard(new_value, static_storage_addr); + } + } + + return; +} + +llvm::Value* GBCExpanderPass::Expand_ConstString(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t string_idx = LV2UInt(call_inst.getArgOperand(0)); + + llvm::Value* string_field_addr = EmitLoadDexCacheStringFieldAddr(string_idx); + + llvm::Value* string_addr = irb_.CreateLoad(string_field_addr, kTBAARuntimeInfo); + + if (!driver_->CanAssumeStringIsPresentInDexCache(*dex_compilation_unit_->GetDexFile(), + string_idx)) { + llvm::BasicBlock* block_str_exist = + CreateBasicBlockWithDexPC(dex_pc, "str_exist"); + + llvm::BasicBlock* block_str_resolve = + CreateBasicBlockWithDexPC(dex_pc, "str_resolve"); + + llvm::BasicBlock* block_cont = + CreateBasicBlockWithDexPC(dex_pc, "str_cont"); + + // Test: Is the string resolved and in the dex cache? + llvm::Value* equal_null = irb_.CreateICmpEQ(string_addr, irb_.getJNull()); + + irb_.CreateCondBr(equal_null, block_str_resolve, block_str_exist, kUnlikely); + + // String is resolved, go to next basic block. + irb_.SetInsertPoint(block_str_exist); + irb_.CreateBr(block_cont); + + // String is not resolved yet, resolve it now. + irb_.SetInsertPoint(block_str_resolve); + + llvm::Function* runtime_func = irb_.GetRuntime(runtime_support::ResolveString); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* string_idx_value = irb_.getInt32(string_idx); + + EmitUpdateDexPC(dex_pc); + + llvm::Value* result = irb_.CreateCall2(runtime_func, method_object_addr, + string_idx_value); + + EmitGuard_ExceptionLandingPad(dex_pc); + + irb_.CreateBr(block_cont); + + + llvm::BasicBlock* block_pre_cont = irb_.GetInsertBlock(); + + irb_.SetInsertPoint(block_cont); + + llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2); + + phi->addIncoming(string_addr, block_str_exist); + phi->addIncoming(result, block_pre_cont); + + string_addr = phi; + } + + return string_addr; +} + +llvm::Value* GBCExpanderPass::Expand_ConstClass(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0)); + + llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx); + + return type_object_addr; +} + +void GBCExpanderPass::Expand_MonitorEnter(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + llvm::Value* object_addr = call_inst.getArgOperand(1); + int opt_flags = LV2UInt(call_inst.getArgOperand(0)); + + EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags); + + EmitUpdateDexPC(dex_pc); + + irb_.Runtime().EmitLockObject(object_addr); + + return; +} + +void GBCExpanderPass::Expand_MonitorExit(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + llvm::Value* object_addr = call_inst.getArgOperand(1); + int opt_flags = LV2UInt(call_inst.getArgOperand(0)); + + EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags); + + EmitUpdateDexPC(dex_pc); + + irb_.Runtime().EmitUnlockObject(object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + return; +} + +void GBCExpanderPass::Expand_HLCheckCast(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0)); + llvm::Value* object_addr = call_inst.getArgOperand(1); + + llvm::BasicBlock* block_test_class = + CreateBasicBlockWithDexPC(dex_pc, "test_class"); + + llvm::BasicBlock* block_test_sub_class = + CreateBasicBlockWithDexPC(dex_pc, "test_sub_class"); + + llvm::BasicBlock* block_cont = + CreateBasicBlockWithDexPC(dex_pc, "checkcast_cont"); + + // Test: Is the reference equal to null? Act as no-op when it is null. + llvm::Value* equal_null = irb_.CreateICmpEQ(object_addr, irb_.getJNull()); + + irb_.CreateCondBr(equal_null, block_cont, block_test_class, kUnlikely); + + // Test: Is the object instantiated from the given class? + irb_.SetInsertPoint(block_test_class); + llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx); + DCHECK_EQ(art::mirror::Object::ClassOffset().Int32Value(), 0); + + llvm::PointerType* jobject_ptr_ty = irb_.getJObjectTy(); + + llvm::Value* object_type_field_addr = + irb_.CreateBitCast(object_addr, jobject_ptr_ty->getPointerTo()); + + llvm::Value* object_type_object_addr = + irb_.CreateLoad(object_type_field_addr, kTBAAConstJObject); + + llvm::Value* equal_class = + irb_.CreateICmpEQ(type_object_addr, object_type_object_addr); + + irb_.CreateCondBr(equal_class, block_cont, block_test_sub_class, kLikely); + + // Test: Is the object instantiated from the subclass of the given class? + irb_.SetInsertPoint(block_test_sub_class); + + EmitUpdateDexPC(dex_pc); + + irb_.CreateCall2(irb_.GetRuntime(runtime_support::CheckCast), + type_object_addr, object_type_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + irb_.CreateBr(block_cont); + + irb_.SetInsertPoint(block_cont); + + return; +} + +llvm::Value* GBCExpanderPass::Expand_InstanceOf(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0)); + llvm::Value* object_addr = call_inst.getArgOperand(1); + + llvm::BasicBlock* block_nullp = + CreateBasicBlockWithDexPC(dex_pc, "nullp"); + + llvm::BasicBlock* block_test_class = + CreateBasicBlockWithDexPC(dex_pc, "test_class"); + + llvm::BasicBlock* block_class_equals = + CreateBasicBlockWithDexPC(dex_pc, "class_eq"); + + llvm::BasicBlock* block_test_sub_class = + CreateBasicBlockWithDexPC(dex_pc, "test_sub_class"); + + llvm::BasicBlock* block_cont = + CreateBasicBlockWithDexPC(dex_pc, "instance_of_cont"); + + // Overview of the following code : + // We check for null, if so, then false, otherwise check for class == . If so + // then true, otherwise do callout slowpath. + // + // Test: Is the reference equal to null? Set 0 when it is null. + llvm::Value* equal_null = irb_.CreateICmpEQ(object_addr, irb_.getJNull()); + + irb_.CreateCondBr(equal_null, block_nullp, block_test_class, kUnlikely); + + irb_.SetInsertPoint(block_nullp); + irb_.CreateBr(block_cont); + + // Test: Is the object instantiated from the given class? + irb_.SetInsertPoint(block_test_class); + llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx); + DCHECK_EQ(art::mirror::Object::ClassOffset().Int32Value(), 0); + + llvm::PointerType* jobject_ptr_ty = irb_.getJObjectTy(); + + llvm::Value* object_type_field_addr = + irb_.CreateBitCast(object_addr, jobject_ptr_ty->getPointerTo()); + + llvm::Value* object_type_object_addr = + irb_.CreateLoad(object_type_field_addr, kTBAAConstJObject); + + llvm::Value* equal_class = + irb_.CreateICmpEQ(type_object_addr, object_type_object_addr); + + irb_.CreateCondBr(equal_class, block_class_equals, block_test_sub_class, kLikely); + + irb_.SetInsertPoint(block_class_equals); + irb_.CreateBr(block_cont); + + // Test: Is the object instantiated from the subclass of the given class? + irb_.SetInsertPoint(block_test_sub_class); + llvm::Value* result = + irb_.CreateCall2(irb_.GetRuntime(runtime_support::IsAssignable), + type_object_addr, object_type_object_addr); + irb_.CreateBr(block_cont); + + irb_.SetInsertPoint(block_cont); + + llvm::PHINode* phi = irb_.CreatePHI(irb_.getJIntTy(), 3); + + phi->addIncoming(irb_.getJInt(0), block_nullp); + phi->addIncoming(irb_.getJInt(1), block_class_equals); + phi->addIncoming(result, block_test_sub_class); + + return phi; +} + +llvm::Value* GBCExpanderPass::Expand_NewInstance(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0)); + + llvm::Function* runtime_func; + if (driver_->CanAccessInstantiableTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(), + *dex_compilation_unit_->GetDexFile(), + type_idx)) { + runtime_func = irb_.GetRuntime(runtime_support::AllocObject); + } else { + runtime_func = irb_.GetRuntime(runtime_support::AllocObjectWithAccessCheck); + } + + llvm::Constant* type_index_value = irb_.getInt32(type_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread(); + + EmitUpdateDexPC(dex_pc); + + llvm::Value* object_addr = + irb_.CreateCall3(runtime_func, type_index_value, method_object_addr, thread_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + return object_addr; +} + +llvm::Value* GBCExpanderPass::Expand_HLInvoke(llvm::CallInst& call_inst) { + art::InvokeType invoke_type = static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0))); + bool is_static = (invoke_type == art::kStatic); + + if (!is_static) { + // Test: Is *this* parameter equal to null? + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + llvm::Value* this_addr = call_inst.getArgOperand(3); + int opt_flags = LV2UInt(call_inst.getArgOperand(2)); + + EmitGuard_NullPointerException(dex_pc, this_addr, opt_flags); + } + + llvm::Value* result = NULL; + if (EmitIntrinsic(call_inst, &result)) { + return result; + } + + return EmitInvoke(call_inst); +} + +llvm::Value* GBCExpanderPass::Expand_OptArrayLength(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + // Get the array object address + llvm::Value* array_addr = call_inst.getArgOperand(1); + int opt_flags = LV2UInt(call_inst.getArgOperand(0)); + + EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags); + + // Get the array length and store it to the register + return EmitLoadArrayLength(array_addr); +} + +llvm::Value* GBCExpanderPass::Expand_NewArray(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0)); + llvm::Value* length = call_inst.getArgOperand(1); + + return EmitAllocNewArray(dex_pc, length, type_idx, false); +} + +llvm::Value* GBCExpanderPass::Expand_HLFilledNewArray(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + uint32_t type_idx = LV2UInt(call_inst.getArgOperand(1)); + uint32_t length = call_inst.getNumArgOperands() - 3; + + llvm::Value* object_addr = + EmitAllocNewArray(dex_pc, irb_.getInt32(length), type_idx, true); + + if (length > 0) { + // Check for the element type + uint32_t type_desc_len = 0; + const char* type_desc = + dex_compilation_unit_->GetDexFile()->StringByTypeIdx(type_idx, &type_desc_len); + + DCHECK_GE(type_desc_len, 2u); // should be guaranteed by verifier + DCHECK_EQ(type_desc[0], '['); // should be guaranteed by verifier + bool is_elem_int_ty = (type_desc[1] == 'I'); + + uint32_t alignment; + llvm::Constant* elem_size; + llvm::PointerType* field_type; + + // NOTE: Currently filled-new-array only supports 'L', '[', and 'I' + // as the element, thus we are only checking 2 cases: primitive int and + // non-primitive type. + if (is_elem_int_ty) { + alignment = sizeof(int32_t); + elem_size = irb_.getPtrEquivInt(sizeof(int32_t)); + field_type = irb_.getJIntTy()->getPointerTo(); + } else { + alignment = irb_.getSizeOfPtrEquivInt(); + elem_size = irb_.getSizeOfPtrEquivIntValue(); + field_type = irb_.getJObjectTy()->getPointerTo(); + } + + llvm::Value* data_field_offset = + irb_.getPtrEquivInt(art::mirror::Array::DataOffset(alignment).Int32Value()); + + llvm::Value* data_field_addr = + irb_.CreatePtrDisp(object_addr, data_field_offset, field_type); + + // TODO: Tune this code. Currently we are generating one instruction for + // one element which may be very space consuming. Maybe changing to use + // memcpy may help; however, since we can't guarantee that the alloca of + // dalvik register are continuous, we can't perform such optimization yet. + for (uint32_t i = 0; i < length; ++i) { + llvm::Value* reg_value = call_inst.getArgOperand(i+3); + + irb_.CreateStore(reg_value, data_field_addr, kTBAAHeapArray); + + data_field_addr = + irb_.CreatePtrDisp(data_field_addr, elem_size, field_type); + } + } + + return object_addr; +} + +void GBCExpanderPass::Expand_HLFillArrayData(llvm::CallInst& call_inst) { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + int32_t payload_offset = static_cast<int32_t>(dex_pc) + + LV2SInt(call_inst.getArgOperand(0)); + llvm::Value* array_addr = call_inst.getArgOperand(1); + + const art::Instruction::ArrayDataPayload* payload = + reinterpret_cast<const art::Instruction::ArrayDataPayload*>( + dex_compilation_unit_->GetCodeItem()->insns_ + payload_offset); + + if (payload->element_count == 0) { + // When the number of the elements in the payload is zero, we don't have + // to copy any numbers. However, we should check whether the array object + // address is equal to null or not. + EmitGuard_NullPointerException(dex_pc, array_addr, 0); + } else { + // To save the code size, we are going to call the runtime function to + // copy the content from DexFile. + + // NOTE: We will check for the NullPointerException in the runtime. + + llvm::Function* runtime_func = irb_.GetRuntime(runtime_support::FillArrayData); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + EmitUpdateDexPC(dex_pc); + + irb_.CreateCall4(runtime_func, + method_object_addr, irb_.getInt32(dex_pc), + array_addr, irb_.getInt32(payload_offset)); + + EmitGuard_ExceptionLandingPad(dex_pc); + } + + return; +} + +llvm::Value* GBCExpanderPass::EmitAllocNewArray(uint32_t dex_pc, + llvm::Value* array_length_value, + uint32_t type_idx, + bool is_filled_new_array) { + llvm::Function* runtime_func; + + bool skip_access_check = + driver_->CanAccessTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(), + *dex_compilation_unit_->GetDexFile(), type_idx); + + + if (is_filled_new_array) { + runtime_func = skip_access_check ? + irb_.GetRuntime(runtime_support::CheckAndAllocArray) : + irb_.GetRuntime(runtime_support::CheckAndAllocArrayWithAccessCheck); + } else { + runtime_func = skip_access_check ? + irb_.GetRuntime(runtime_support::AllocArray) : + irb_.GetRuntime(runtime_support::AllocArrayWithAccessCheck); + } + + llvm::Constant* type_index_value = irb_.getInt32(type_idx); + + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread(); + + EmitUpdateDexPC(dex_pc); + + llvm::Value* object_addr = + irb_.CreateCall4(runtime_func, type_index_value, method_object_addr, + array_length_value, thread_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + return object_addr; +} + +llvm::Value* GBCExpanderPass:: +EmitCallRuntimeForCalleeMethodObjectAddr(uint32_t callee_method_idx, + art::InvokeType invoke_type, + llvm::Value* this_addr, + uint32_t dex_pc, + bool is_fast_path) { + + llvm::Function* runtime_func = NULL; + + switch (invoke_type) { + case art::kStatic: + runtime_func = irb_.GetRuntime(runtime_support::FindStaticMethodWithAccessCheck); + break; + + case art::kDirect: + runtime_func = irb_.GetRuntime(runtime_support::FindDirectMethodWithAccessCheck); + break; + + case art::kVirtual: + runtime_func = irb_.GetRuntime(runtime_support::FindVirtualMethodWithAccessCheck); + break; + + case art::kSuper: + runtime_func = irb_.GetRuntime(runtime_support::FindSuperMethodWithAccessCheck); + break; + + case art::kInterface: + if (is_fast_path) { + runtime_func = irb_.GetRuntime(runtime_support::FindInterfaceMethod); + } else { + runtime_func = irb_.GetRuntime(runtime_support::FindInterfaceMethodWithAccessCheck); + } + break; + } + + llvm::Value* callee_method_idx_value = irb_.getInt32(callee_method_idx); + + if (this_addr == NULL) { + DCHECK_EQ(invoke_type, art::kStatic); + this_addr = irb_.getJNull(); + } + + llvm::Value* caller_method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread(); + + EmitUpdateDexPC(dex_pc); + + llvm::Value* callee_method_object_addr = + irb_.CreateCall4(runtime_func, + callee_method_idx_value, + this_addr, + caller_method_object_addr, + thread_object_addr); + + EmitGuard_ExceptionLandingPad(dex_pc); + + return callee_method_object_addr; +} + +void GBCExpanderPass::EmitMarkGCCard(llvm::Value* value, llvm::Value* target_addr) { + // Using runtime support, let the target can override by InlineAssembly. + irb_.Runtime().EmitMarkGCCard(value, target_addr); +} + +void GBCExpanderPass::EmitUpdateDexPC(uint32_t dex_pc) { + if (shadow_frame_ == NULL) { + return; + } + irb_.StoreToObjectOffset(shadow_frame_, + art::ShadowFrame::DexPCOffset(), + irb_.getInt32(dex_pc), + kTBAAShadowFrame); +} + +void GBCExpanderPass::EmitGuard_DivZeroException(uint32_t dex_pc, + llvm::Value* denominator, + JType op_jty) { + DCHECK(op_jty == kInt || op_jty == kLong) << op_jty; + + llvm::Constant* zero = irb_.getJZero(op_jty); + + llvm::Value* equal_zero = irb_.CreateICmpEQ(denominator, zero); + + llvm::BasicBlock* block_exception = CreateBasicBlockWithDexPC(dex_pc, "div0"); + + llvm::BasicBlock* block_continue = CreateBasicBlockWithDexPC(dex_pc, "cont"); + + irb_.CreateCondBr(equal_zero, block_exception, block_continue, kUnlikely); + + irb_.SetInsertPoint(block_exception); + EmitUpdateDexPC(dex_pc); + irb_.CreateCall(irb_.GetRuntime(runtime_support::ThrowDivZeroException)); + EmitBranchExceptionLandingPad(dex_pc); + + irb_.SetInsertPoint(block_continue); +} + +void GBCExpanderPass::EmitGuard_NullPointerException(uint32_t dex_pc, + llvm::Value* object, + int opt_flags) { + bool ignore_null_check = ((opt_flags & MIR_IGNORE_NULL_CHECK) != 0); + if (ignore_null_check) { + llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc); + if (lpad) { + // There is at least one catch: create a "fake" conditional branch to + // keep the exception edge to the catch block. + landing_pad_phi_mapping_[lpad].push_back( + std::make_pair(current_bb_->getUniquePredecessor(), + irb_.GetInsertBlock())); + + llvm::BasicBlock* block_continue = + CreateBasicBlockWithDexPC(dex_pc, "cont"); + + irb_.CreateCondBr(irb_.getFalse(), lpad, block_continue, kUnlikely); + + irb_.SetInsertPoint(block_continue); + } + } else { + llvm::Value* equal_null = irb_.CreateICmpEQ(object, irb_.getJNull()); + + llvm::BasicBlock* block_exception = + CreateBasicBlockWithDexPC(dex_pc, "nullp"); + + llvm::BasicBlock* block_continue = + CreateBasicBlockWithDexPC(dex_pc, "cont"); + + irb_.CreateCondBr(equal_null, block_exception, block_continue, kUnlikely); + + irb_.SetInsertPoint(block_exception); + EmitUpdateDexPC(dex_pc); + irb_.CreateCall(irb_.GetRuntime(runtime_support::ThrowNullPointerException), + irb_.getInt32(dex_pc)); + EmitBranchExceptionLandingPad(dex_pc); + + irb_.SetInsertPoint(block_continue); + } +} + +void +GBCExpanderPass::EmitGuard_ArrayIndexOutOfBoundsException(uint32_t dex_pc, + llvm::Value* array, + llvm::Value* index, + int opt_flags) { + bool ignore_range_check = ((opt_flags & MIR_IGNORE_RANGE_CHECK) != 0); + if (ignore_range_check) { + llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc); + if (lpad) { + // There is at least one catch: create a "fake" conditional branch to + // keep the exception edge to the catch block. + landing_pad_phi_mapping_[lpad].push_back( + std::make_pair(current_bb_->getUniquePredecessor(), + irb_.GetInsertBlock())); + + llvm::BasicBlock* block_continue = + CreateBasicBlockWithDexPC(dex_pc, "cont"); + + irb_.CreateCondBr(irb_.getFalse(), lpad, block_continue, kUnlikely); + + irb_.SetInsertPoint(block_continue); + } + } else { + llvm::Value* array_len = EmitLoadArrayLength(array); + + llvm::Value* cmp = irb_.CreateICmpUGE(index, array_len); + + llvm::BasicBlock* block_exception = + CreateBasicBlockWithDexPC(dex_pc, "overflow"); + + llvm::BasicBlock* block_continue = + CreateBasicBlockWithDexPC(dex_pc, "cont"); + + irb_.CreateCondBr(cmp, block_exception, block_continue, kUnlikely); + + irb_.SetInsertPoint(block_exception); + + EmitUpdateDexPC(dex_pc); + irb_.CreateCall2(irb_.GetRuntime(runtime_support::ThrowIndexOutOfBounds), index, array_len); + EmitBranchExceptionLandingPad(dex_pc); + + irb_.SetInsertPoint(block_continue); + } +} + +llvm::FunctionType* GBCExpanderPass::GetFunctionType(llvm::Type* ret_type, uint32_t method_idx, + bool is_static) { + // Get method signature + art::DexFile::MethodId const& method_id = + dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx); + + uint32_t shorty_size; + const char* shorty = dex_compilation_unit_->GetDexFile()->GetMethodShorty(method_id, &shorty_size); + CHECK_GE(shorty_size, 1u); + + // Get argument type + std::vector<llvm::Type*> args_type; + + args_type.push_back(irb_.getJObjectTy()); // method object pointer + + if (!is_static) { + args_type.push_back(irb_.getJType('L')); // "this" object pointer + } + + for (uint32_t i = 1; i < shorty_size; ++i) { + char shorty_type = art::RemapShorty(shorty[i]); + args_type.push_back(irb_.getJType(shorty_type)); + } + + return llvm::FunctionType::get(ret_type, args_type, false); +} + + +llvm::BasicBlock* GBCExpanderPass:: +CreateBasicBlockWithDexPC(uint32_t dex_pc, const char* postfix) { + std::string name; + +#if !defined(NDEBUG) + art::StringAppendF(&name, "B%04x.%s", dex_pc, postfix); +#endif + + return llvm::BasicBlock::Create(context_, name, func_); +} + +llvm::BasicBlock* GBCExpanderPass::GetBasicBlock(uint32_t dex_pc) { + DCHECK(dex_pc < dex_compilation_unit_->GetCodeItem()->insns_size_in_code_units_); + CHECK(basic_blocks_[dex_pc] != NULL); + return basic_blocks_[dex_pc]; +} + +int32_t GBCExpanderPass::GetTryItemOffset(uint32_t dex_pc) { + int32_t min = 0; + int32_t max = dex_compilation_unit_->GetCodeItem()->tries_size_ - 1; + + while (min <= max) { + int32_t mid = min + (max - min) / 2; + + const art::DexFile::TryItem* ti = + art::DexFile::GetTryItems(*dex_compilation_unit_->GetCodeItem(), mid); + uint32_t start = ti->start_addr_; + uint32_t end = start + ti->insn_count_; + + if (dex_pc < start) { + max = mid - 1; + } else if (dex_pc >= end) { + min = mid + 1; + } else { + return mid; // found + } + } + + return -1; // not found +} + +llvm::BasicBlock* GBCExpanderPass::GetLandingPadBasicBlock(uint32_t dex_pc) { + // Find the try item for this address in this method + int32_t ti_offset = GetTryItemOffset(dex_pc); + + if (ti_offset == -1) { + return NULL; // No landing pad is available for this address. + } + + // Check for the existing landing pad basic block + DCHECK_GT(basic_block_landing_pads_.size(), static_cast<size_t>(ti_offset)); + llvm::BasicBlock* block_lpad = basic_block_landing_pads_[ti_offset]; + + if (block_lpad) { + // We have generated landing pad for this try item already. Return the + // same basic block. + return block_lpad; + } + + // Get try item from code item + const art::DexFile::TryItem* ti = art::DexFile::GetTryItems(*dex_compilation_unit_->GetCodeItem(), + ti_offset); + + std::string lpadname; + +#if !defined(NDEBUG) + art::StringAppendF(&lpadname, "lpad%d_%04x_to_%04x", ti_offset, ti->start_addr_, ti->handler_off_); +#endif + + // Create landing pad basic block + block_lpad = llvm::BasicBlock::Create(context_, lpadname, func_); + + // Change IRBuilder insert point + llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP(); + irb_.SetInsertPoint(block_lpad); + + // Find catch block with matching type + llvm::Value* method_object_addr = EmitLoadMethodObjectAddr(); + + llvm::Value* ti_offset_value = irb_.getInt32(ti_offset); + + llvm::Value* catch_handler_index_value = + irb_.CreateCall2(irb_.GetRuntime(runtime_support::FindCatchBlock), + method_object_addr, ti_offset_value); + + // Switch instruction (Go to unwind basic block by default) + llvm::SwitchInst* sw = + irb_.CreateSwitch(catch_handler_index_value, GetUnwindBasicBlock()); + + // Cases with matched catch block + art::CatchHandlerIterator iter(*dex_compilation_unit_->GetCodeItem(), ti->start_addr_); + + for (uint32_t c = 0; iter.HasNext(); iter.Next(), ++c) { + sw->addCase(irb_.getInt32(c), GetBasicBlock(iter.GetHandlerAddress())); + } + + // Restore the orignal insert point for IRBuilder + irb_.restoreIP(irb_ip_original); + + // Cache this landing pad + DCHECK_GT(basic_block_landing_pads_.size(), static_cast<size_t>(ti_offset)); + basic_block_landing_pads_[ti_offset] = block_lpad; + + return block_lpad; +} + +llvm::BasicBlock* GBCExpanderPass::GetUnwindBasicBlock() { + // Check the existing unwinding baisc block block + if (basic_block_unwind_ != NULL) { + return basic_block_unwind_; + } + + // Create new basic block for unwinding + basic_block_unwind_ = + llvm::BasicBlock::Create(context_, "exception_unwind", func_); + + // Change IRBuilder insert point + llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP(); + irb_.SetInsertPoint(basic_block_unwind_); + + // Pop the shadow frame + Expand_PopShadowFrame(); + + // Emit the code to return default value (zero) for the given return type. + char ret_shorty = dex_compilation_unit_->GetShorty()[0]; + ret_shorty = art::RemapShorty(ret_shorty); + if (ret_shorty == 'V') { + irb_.CreateRetVoid(); + } else { + irb_.CreateRet(irb_.getJZero(ret_shorty)); + } + + // Restore the orignal insert point for IRBuilder + irb_.restoreIP(irb_ip_original); + + return basic_block_unwind_; +} + +void GBCExpanderPass::EmitBranchExceptionLandingPad(uint32_t dex_pc) { + if (llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc)) { + landing_pad_phi_mapping_[lpad].push_back(std::make_pair(current_bb_->getUniquePredecessor(), + irb_.GetInsertBlock())); + irb_.CreateBr(lpad); + } else { + irb_.CreateBr(GetUnwindBasicBlock()); + } +} + +void GBCExpanderPass::EmitGuard_ExceptionLandingPad(uint32_t dex_pc) { + llvm::Value* exception_pending = irb_.Runtime().EmitIsExceptionPending(); + + llvm::BasicBlock* block_cont = CreateBasicBlockWithDexPC(dex_pc, "cont"); + + if (llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc)) { + landing_pad_phi_mapping_[lpad].push_back(std::make_pair(current_bb_->getUniquePredecessor(), + irb_.GetInsertBlock())); + irb_.CreateCondBr(exception_pending, lpad, block_cont, kUnlikely); + } else { + irb_.CreateCondBr(exception_pending, GetUnwindBasicBlock(), block_cont, kUnlikely); + } + + irb_.SetInsertPoint(block_cont); +} + +llvm::Value* +GBCExpanderPass::ExpandIntrinsic(IntrinsicHelper::IntrinsicId intr_id, + llvm::CallInst& call_inst) { + switch (intr_id) { + //==- Thread -----------------------------------------------------------==// + case IntrinsicHelper::GetCurrentThread: { + return irb_.Runtime().EmitGetCurrentThread(); + } + case IntrinsicHelper::CheckSuspend: { + Expand_TestSuspend(call_inst); + return NULL; + } + case IntrinsicHelper::TestSuspend: { + Expand_TestSuspend(call_inst); + return NULL; + } + case IntrinsicHelper::MarkGCCard: { + Expand_MarkGCCard(call_inst); + return NULL; + } + + //==- Exception --------------------------------------------------------==// + case IntrinsicHelper::ThrowException: { + return ExpandToRuntime(runtime_support::ThrowException, call_inst); + } + case IntrinsicHelper::HLThrowException: { + uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); + + EmitUpdateDexPC(dex_pc); + + irb_.CreateCall(irb_.GetRuntime(runtime_support::ThrowException), + call_inst.getArgOperand(0)); + + EmitGuard_ExceptionLandingPad(dex_pc); + return NULL; + } + case IntrinsicHelper::GetException: { + return irb_.Runtime().EmitGetAndClearException(); + } + case IntrinsicHelper::IsExceptionPending: { + return irb_.Runtime().EmitIsExceptionPending(); + } + case IntrinsicHelper::FindCatchBlock: { + return ExpandToRuntime(runtime_support::FindCatchBlock, call_inst); + } + case IntrinsicHelper::ThrowDivZeroException: { + return ExpandToRuntime(runtime_support::ThrowDivZeroException, call_inst); + } + case IntrinsicHelper::ThrowNullPointerException: { + return ExpandToRuntime(runtime_support::ThrowNullPointerException, call_inst); + } + case IntrinsicHelper::ThrowIndexOutOfBounds: { + return ExpandToRuntime(runtime_support::ThrowIndexOutOfBounds, call_inst); + } + + //==- Const String -----------------------------------------------------==// + case IntrinsicHelper::ConstString: { + return Expand_ConstString(call_inst); + } + case IntrinsicHelper::LoadStringFromDexCache: { + return Expand_LoadStringFromDexCache(call_inst.getArgOperand(0)); + } + case IntrinsicHelper::ResolveString: { + return ExpandToRuntime(runtime_support::ResolveString, call_inst); + } + + //==- Const Class ------------------------------------------------------==// + case IntrinsicHelper::ConstClass: { + return Expand_ConstClass(call_inst); + } + case IntrinsicHelper::InitializeTypeAndVerifyAccess: { + return ExpandToRuntime(runtime_support::InitializeTypeAndVerifyAccess, call_inst); + } + case IntrinsicHelper::LoadTypeFromDexCache: { + return Expand_LoadTypeFromDexCache(call_inst.getArgOperand(0)); + } + case IntrinsicHelper::InitializeType: { + return ExpandToRuntime(runtime_support::InitializeType, call_inst); + } + + //==- Lock -------------------------------------------------------------==// + case IntrinsicHelper::LockObject: { + Expand_LockObject(call_inst.getArgOperand(0)); + return NULL; + } + case IntrinsicHelper::UnlockObject: { + Expand_UnlockObject(call_inst.getArgOperand(0)); + return NULL; + } + + //==- Cast -------------------------------------------------------------==// + case IntrinsicHelper::CheckCast: { + return ExpandToRuntime(runtime_support::CheckCast, call_inst); + } + case IntrinsicHelper::HLCheckCast: { + Expand_HLCheckCast(call_inst); + return NULL; + } + case IntrinsicHelper::IsAssignable: { + return ExpandToRuntime(runtime_support::IsAssignable, call_inst); + } + + //==- Alloc ------------------------------------------------------------==// + case IntrinsicHelper::AllocObject: { + return ExpandToRuntime(runtime_support::AllocObject, call_inst); + } + case IntrinsicHelper::AllocObjectWithAccessCheck: { + return ExpandToRuntime(runtime_support::AllocObjectWithAccessCheck, call_inst); + } + + //==- Instance ---------------------------------------------------------==// + case IntrinsicHelper::NewInstance: { + return Expand_NewInstance(call_inst); + } + case IntrinsicHelper::InstanceOf: { + return Expand_InstanceOf(call_inst); + } + + //==- Array ------------------------------------------------------------==// + case IntrinsicHelper::NewArray: { + return Expand_NewArray(call_inst); + } + case IntrinsicHelper::OptArrayLength: { + return Expand_OptArrayLength(call_inst); + } + case IntrinsicHelper::ArrayLength: { + return EmitLoadArrayLength(call_inst.getArgOperand(0)); + } + case IntrinsicHelper::AllocArray: { + return ExpandToRuntime(runtime_support::AllocArray, call_inst); + } + case IntrinsicHelper::AllocArrayWithAccessCheck: { + return ExpandToRuntime(runtime_support::AllocArrayWithAccessCheck, + call_inst); + } + case IntrinsicHelper::CheckAndAllocArray: { + return ExpandToRuntime(runtime_support::CheckAndAllocArray, call_inst); + } + case IntrinsicHelper::CheckAndAllocArrayWithAccessCheck: { + return ExpandToRuntime(runtime_support::CheckAndAllocArrayWithAccessCheck, + call_inst); + } + case IntrinsicHelper::ArrayGet: { + return Expand_ArrayGet(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kInt); + } + case IntrinsicHelper::ArrayGetWide: { + return Expand_ArrayGet(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kLong); + } + case IntrinsicHelper::ArrayGetObject: { + return Expand_ArrayGet(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kObject); + } + case IntrinsicHelper::ArrayGetBoolean: { + return Expand_ArrayGet(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kBoolean); + } + case IntrinsicHelper::ArrayGetByte: { + return Expand_ArrayGet(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kByte); + } + case IntrinsicHelper::ArrayGetChar: { + return Expand_ArrayGet(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kChar); + } + case IntrinsicHelper::ArrayGetShort: { + return Expand_ArrayGet(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kShort); + } + case IntrinsicHelper::ArrayPut: { + Expand_ArrayPut(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kInt); + return NULL; + } + case IntrinsicHelper::ArrayPutWide: { + Expand_ArrayPut(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kLong); + return NULL; + } + case IntrinsicHelper::ArrayPutObject: { + Expand_ArrayPut(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kObject); + return NULL; + } + case IntrinsicHelper::ArrayPutBoolean: { + Expand_ArrayPut(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kBoolean); + return NULL; + } + case IntrinsicHelper::ArrayPutByte: { + Expand_ArrayPut(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kByte); + return NULL; + } + case IntrinsicHelper::ArrayPutChar: { + Expand_ArrayPut(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kChar); + return NULL; + } + case IntrinsicHelper::ArrayPutShort: { + Expand_ArrayPut(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kShort); + return NULL; + } + case IntrinsicHelper::CheckPutArrayElement: { + return ExpandToRuntime(runtime_support::CheckPutArrayElement, call_inst); + } + case IntrinsicHelper::FilledNewArray: { + Expand_FilledNewArray(call_inst); + return NULL; + } + case IntrinsicHelper::FillArrayData: { + return ExpandToRuntime(runtime_support::FillArrayData, call_inst); + } + case IntrinsicHelper::HLFillArrayData: { + Expand_HLFillArrayData(call_inst); + return NULL; + } + case IntrinsicHelper::HLFilledNewArray: { + return Expand_HLFilledNewArray(call_inst); + } + + //==- Instance Field ---------------------------------------------------==// + case IntrinsicHelper::InstanceFieldGet: + case IntrinsicHelper::InstanceFieldGetBoolean: + case IntrinsicHelper::InstanceFieldGetByte: + case IntrinsicHelper::InstanceFieldGetChar: + case IntrinsicHelper::InstanceFieldGetShort: { + return ExpandToRuntime(runtime_support::Get32Instance, call_inst); + } + case IntrinsicHelper::InstanceFieldGetWide: { + return ExpandToRuntime(runtime_support::Get64Instance, call_inst); + } + case IntrinsicHelper::InstanceFieldGetObject: { + return ExpandToRuntime(runtime_support::GetObjectInstance, call_inst); + } + case IntrinsicHelper::InstanceFieldGetFast: { + return Expand_IGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kInt); + } + case IntrinsicHelper::InstanceFieldGetWideFast: { + return Expand_IGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kLong); + } + case IntrinsicHelper::InstanceFieldGetObjectFast: { + return Expand_IGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kObject); + } + case IntrinsicHelper::InstanceFieldGetBooleanFast: { + return Expand_IGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kBoolean); + } + case IntrinsicHelper::InstanceFieldGetByteFast: { + return Expand_IGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kByte); + } + case IntrinsicHelper::InstanceFieldGetCharFast: { + return Expand_IGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kChar); + } + case IntrinsicHelper::InstanceFieldGetShortFast: { + return Expand_IGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kShort); + } + case IntrinsicHelper::InstanceFieldPut: + case IntrinsicHelper::InstanceFieldPutBoolean: + case IntrinsicHelper::InstanceFieldPutByte: + case IntrinsicHelper::InstanceFieldPutChar: + case IntrinsicHelper::InstanceFieldPutShort: { + return ExpandToRuntime(runtime_support::Set32Instance, call_inst); + } + case IntrinsicHelper::InstanceFieldPutWide: { + return ExpandToRuntime(runtime_support::Set64Instance, call_inst); + } + case IntrinsicHelper::InstanceFieldPutObject: { + return ExpandToRuntime(runtime_support::SetObjectInstance, call_inst); + } + case IntrinsicHelper::InstanceFieldPutFast: { + Expand_IPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kInt); + return NULL; + } + case IntrinsicHelper::InstanceFieldPutWideFast: { + Expand_IPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kLong); + return NULL; + } + case IntrinsicHelper::InstanceFieldPutObjectFast: { + Expand_IPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kObject); + return NULL; + } + case IntrinsicHelper::InstanceFieldPutBooleanFast: { + Expand_IPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kBoolean); + return NULL; + } + case IntrinsicHelper::InstanceFieldPutByteFast: { + Expand_IPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kByte); + return NULL; + } + case IntrinsicHelper::InstanceFieldPutCharFast: { + Expand_IPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kChar); + return NULL; + } + case IntrinsicHelper::InstanceFieldPutShortFast: { + Expand_IPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kShort); + return NULL; + } + + //==- Static Field -----------------------------------------------------==// + case IntrinsicHelper::StaticFieldGet: + case IntrinsicHelper::StaticFieldGetBoolean: + case IntrinsicHelper::StaticFieldGetByte: + case IntrinsicHelper::StaticFieldGetChar: + case IntrinsicHelper::StaticFieldGetShort: { + return ExpandToRuntime(runtime_support::Get32Static, call_inst); + } + case IntrinsicHelper::StaticFieldGetWide: { + return ExpandToRuntime(runtime_support::Get64Static, call_inst); + } + case IntrinsicHelper::StaticFieldGetObject: { + return ExpandToRuntime(runtime_support::GetObjectStatic, call_inst); + } + case IntrinsicHelper::StaticFieldGetFast: { + return Expand_SGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kInt); + } + case IntrinsicHelper::StaticFieldGetWideFast: { + return Expand_SGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kLong); + } + case IntrinsicHelper::StaticFieldGetObjectFast: { + return Expand_SGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kObject); + } + case IntrinsicHelper::StaticFieldGetBooleanFast: { + return Expand_SGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kBoolean); + } + case IntrinsicHelper::StaticFieldGetByteFast: { + return Expand_SGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kByte); + } + case IntrinsicHelper::StaticFieldGetCharFast: { + return Expand_SGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kChar); + } + case IntrinsicHelper::StaticFieldGetShortFast: { + return Expand_SGetFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + kShort); + } + case IntrinsicHelper::StaticFieldPut: + case IntrinsicHelper::StaticFieldPutBoolean: + case IntrinsicHelper::StaticFieldPutByte: + case IntrinsicHelper::StaticFieldPutChar: + case IntrinsicHelper::StaticFieldPutShort: { + return ExpandToRuntime(runtime_support::Set32Static, call_inst); + } + case IntrinsicHelper::StaticFieldPutWide: { + return ExpandToRuntime(runtime_support::Set64Static, call_inst); + } + case IntrinsicHelper::StaticFieldPutObject: { + return ExpandToRuntime(runtime_support::SetObjectStatic, call_inst); + } + case IntrinsicHelper::StaticFieldPutFast: { + Expand_SPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kInt); + return NULL; + } + case IntrinsicHelper::StaticFieldPutWideFast: { + Expand_SPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kLong); + return NULL; + } + case IntrinsicHelper::StaticFieldPutObjectFast: { + Expand_SPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kObject); + return NULL; + } + case IntrinsicHelper::StaticFieldPutBooleanFast: { + Expand_SPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kBoolean); + return NULL; + } + case IntrinsicHelper::StaticFieldPutByteFast: { + Expand_SPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kByte); + return NULL; + } + case IntrinsicHelper::StaticFieldPutCharFast: { + Expand_SPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kChar); + return NULL; + } + case IntrinsicHelper::StaticFieldPutShortFast: { + Expand_SPutFast(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + call_inst.getArgOperand(2), + call_inst.getArgOperand(3), + kShort); + return NULL; + } + case IntrinsicHelper::LoadDeclaringClassSSB: { + return Expand_LoadDeclaringClassSSB(call_inst.getArgOperand(0)); + } + case IntrinsicHelper::LoadClassSSBFromDexCache: { + return Expand_LoadClassSSBFromDexCache(call_inst.getArgOperand(0)); + } + case IntrinsicHelper::InitializeAndLoadClassSSB: { + return ExpandToRuntime(runtime_support::InitializeStaticStorage, call_inst); + } + + //==- High-level Array -------------------------------------------------==// + case IntrinsicHelper::HLArrayGet: { + return Expand_HLArrayGet(call_inst, kInt); + } + case IntrinsicHelper::HLArrayGetBoolean: { + return Expand_HLArrayGet(call_inst, kBoolean); + } + case IntrinsicHelper::HLArrayGetByte: { + return Expand_HLArrayGet(call_inst, kByte); + } + case IntrinsicHelper::HLArrayGetChar: { + return Expand_HLArrayGet(call_inst, kChar); + } + case IntrinsicHelper::HLArrayGetShort: { + return Expand_HLArrayGet(call_inst, kShort); + } + case IntrinsicHelper::HLArrayGetFloat: { + return Expand_HLArrayGet(call_inst, kFloat); + } + case IntrinsicHelper::HLArrayGetWide: { + return Expand_HLArrayGet(call_inst, kLong); + } + case IntrinsicHelper::HLArrayGetDouble: { + return Expand_HLArrayGet(call_inst, kDouble); + } + case IntrinsicHelper::HLArrayGetObject: { + return Expand_HLArrayGet(call_inst, kObject); + } + case IntrinsicHelper::HLArrayPut: { + Expand_HLArrayPut(call_inst, kInt); + return NULL; + } + case IntrinsicHelper::HLArrayPutBoolean: { + Expand_HLArrayPut(call_inst, kBoolean); + return NULL; + } + case IntrinsicHelper::HLArrayPutByte: { + Expand_HLArrayPut(call_inst, kByte); + return NULL; + } + case IntrinsicHelper::HLArrayPutChar: { + Expand_HLArrayPut(call_inst, kChar); + return NULL; + } + case IntrinsicHelper::HLArrayPutShort: { + Expand_HLArrayPut(call_inst, kShort); + return NULL; + } + case IntrinsicHelper::HLArrayPutFloat: { + Expand_HLArrayPut(call_inst, kFloat); + return NULL; + } + case IntrinsicHelper::HLArrayPutWide: { + Expand_HLArrayPut(call_inst, kLong); + return NULL; + } + case IntrinsicHelper::HLArrayPutDouble: { + Expand_HLArrayPut(call_inst, kDouble); + return NULL; + } + case IntrinsicHelper::HLArrayPutObject: { + Expand_HLArrayPut(call_inst, kObject); + return NULL; + } + + //==- High-level Instance ----------------------------------------------==// + case IntrinsicHelper::HLIGet: { + return Expand_HLIGet(call_inst, kInt); + } + case IntrinsicHelper::HLIGetBoolean: { + return Expand_HLIGet(call_inst, kBoolean); + } + case IntrinsicHelper::HLIGetByte: { + return Expand_HLIGet(call_inst, kByte); + } + case IntrinsicHelper::HLIGetChar: { + return Expand_HLIGet(call_inst, kChar); + } + case IntrinsicHelper::HLIGetShort: { + return Expand_HLIGet(call_inst, kShort); + } + case IntrinsicHelper::HLIGetFloat: { + return Expand_HLIGet(call_inst, kFloat); + } + case IntrinsicHelper::HLIGetWide: { + return Expand_HLIGet(call_inst, kLong); + } + case IntrinsicHelper::HLIGetDouble: { + return Expand_HLIGet(call_inst, kDouble); + } + case IntrinsicHelper::HLIGetObject: { + return Expand_HLIGet(call_inst, kObject); + } + case IntrinsicHelper::HLIPut: { + Expand_HLIPut(call_inst, kInt); + return NULL; + } + case IntrinsicHelper::HLIPutBoolean: { + Expand_HLIPut(call_inst, kBoolean); + return NULL; + } + case IntrinsicHelper::HLIPutByte: { + Expand_HLIPut(call_inst, kByte); + return NULL; + } + case IntrinsicHelper::HLIPutChar: { + Expand_HLIPut(call_inst, kChar); + return NULL; + } + case IntrinsicHelper::HLIPutShort: { + Expand_HLIPut(call_inst, kShort); + return NULL; + } + case IntrinsicHelper::HLIPutFloat: { + Expand_HLIPut(call_inst, kFloat); + return NULL; + } + case IntrinsicHelper::HLIPutWide: { + Expand_HLIPut(call_inst, kLong); + return NULL; + } + case IntrinsicHelper::HLIPutDouble: { + Expand_HLIPut(call_inst, kDouble); + return NULL; + } + case IntrinsicHelper::HLIPutObject: { + Expand_HLIPut(call_inst, kObject); + return NULL; + } + + //==- High-level Invoke ------------------------------------------------==// + case IntrinsicHelper::HLInvokeVoid: + case IntrinsicHelper::HLInvokeObj: + case IntrinsicHelper::HLInvokeInt: + case IntrinsicHelper::HLInvokeFloat: + case IntrinsicHelper::HLInvokeLong: + case IntrinsicHelper::HLInvokeDouble: { + return Expand_HLInvoke(call_inst); + } + + //==- Invoke -----------------------------------------------------------==// + case IntrinsicHelper::FindStaticMethodWithAccessCheck: { + return ExpandToRuntime(runtime_support::FindStaticMethodWithAccessCheck, call_inst); + } + case IntrinsicHelper::FindDirectMethodWithAccessCheck: { + return ExpandToRuntime(runtime_support::FindDirectMethodWithAccessCheck, call_inst); + } + case IntrinsicHelper::FindVirtualMethodWithAccessCheck: { + return ExpandToRuntime(runtime_support::FindVirtualMethodWithAccessCheck, call_inst); + } + case IntrinsicHelper::FindSuperMethodWithAccessCheck: { + return ExpandToRuntime(runtime_support::FindSuperMethodWithAccessCheck, call_inst); + } + case IntrinsicHelper::FindInterfaceMethodWithAccessCheck: { + return ExpandToRuntime(runtime_support::FindInterfaceMethodWithAccessCheck, call_inst); + } + case IntrinsicHelper::GetSDCalleeMethodObjAddrFast: { + return Expand_GetSDCalleeMethodObjAddrFast(call_inst.getArgOperand(0)); + } + case IntrinsicHelper::GetVirtualCalleeMethodObjAddrFast: { + return Expand_GetVirtualCalleeMethodObjAddrFast( + call_inst.getArgOperand(0), call_inst.getArgOperand(1)); + } + case IntrinsicHelper::GetInterfaceCalleeMethodObjAddrFast: { + return ExpandToRuntime(runtime_support::FindInterfaceMethod, call_inst); + } + case IntrinsicHelper::InvokeRetVoid: + case IntrinsicHelper::InvokeRetBoolean: + case IntrinsicHelper::InvokeRetByte: + case IntrinsicHelper::InvokeRetChar: + case IntrinsicHelper::InvokeRetShort: + case IntrinsicHelper::InvokeRetInt: + case IntrinsicHelper::InvokeRetLong: + case IntrinsicHelper::InvokeRetFloat: + case IntrinsicHelper::InvokeRetDouble: + case IntrinsicHelper::InvokeRetObject: { + return Expand_Invoke(call_inst); + } + + //==- Math -------------------------------------------------------------==// + case IntrinsicHelper::DivInt: { + return Expand_DivRem(call_inst, /* is_div */true, kInt); + } + case IntrinsicHelper::RemInt: { + return Expand_DivRem(call_inst, /* is_div */false, kInt); + } + case IntrinsicHelper::DivLong: { + return Expand_DivRem(call_inst, /* is_div */true, kLong); + } + case IntrinsicHelper::RemLong: { + return Expand_DivRem(call_inst, /* is_div */false, kLong); + } + case IntrinsicHelper::D2L: { + return ExpandToRuntime(runtime_support::art_d2l, call_inst); + } + case IntrinsicHelper::D2I: { + return ExpandToRuntime(runtime_support::art_d2i, call_inst); + } + case IntrinsicHelper::F2L: { + return ExpandToRuntime(runtime_support::art_f2l, call_inst); + } + case IntrinsicHelper::F2I: { + return ExpandToRuntime(runtime_support::art_f2i, call_inst); + } + + //==- High-level Static ------------------------------------------------==// + case IntrinsicHelper::HLSget: { + return Expand_HLSget(call_inst, kInt); + } + case IntrinsicHelper::HLSgetBoolean: { + return Expand_HLSget(call_inst, kBoolean); + } + case IntrinsicHelper::HLSgetByte: { + return Expand_HLSget(call_inst, kByte); + } + case IntrinsicHelper::HLSgetChar: { + return Expand_HLSget(call_inst, kChar); + } + case IntrinsicHelper::HLSgetShort: { + return Expand_HLSget(call_inst, kShort); + } + case IntrinsicHelper::HLSgetFloat: { + return Expand_HLSget(call_inst, kFloat); + } + case IntrinsicHelper::HLSgetWide: { + return Expand_HLSget(call_inst, kLong); + } + case IntrinsicHelper::HLSgetDouble: { + return Expand_HLSget(call_inst, kDouble); + } + case IntrinsicHelper::HLSgetObject: { + return Expand_HLSget(call_inst, kObject); + } + case IntrinsicHelper::HLSput: { + Expand_HLSput(call_inst, kInt); + return NULL; + } + case IntrinsicHelper::HLSputBoolean: { + Expand_HLSput(call_inst, kBoolean); + return NULL; + } + case IntrinsicHelper::HLSputByte: { + Expand_HLSput(call_inst, kByte); + return NULL; + } + case IntrinsicHelper::HLSputChar: { + Expand_HLSput(call_inst, kChar); + return NULL; + } + case IntrinsicHelper::HLSputShort: { + Expand_HLSput(call_inst, kShort); + return NULL; + } + case IntrinsicHelper::HLSputFloat: { + Expand_HLSput(call_inst, kFloat); + return NULL; + } + case IntrinsicHelper::HLSputWide: { + Expand_HLSput(call_inst, kLong); + return NULL; + } + case IntrinsicHelper::HLSputDouble: { + Expand_HLSput(call_inst, kDouble); + return NULL; + } + case IntrinsicHelper::HLSputObject: { + Expand_HLSput(call_inst, kObject); + return NULL; + } + + //==- High-level Monitor -----------------------------------------------==// + case IntrinsicHelper::MonitorEnter: { + Expand_MonitorEnter(call_inst); + return NULL; + } + case IntrinsicHelper::MonitorExit: { + Expand_MonitorExit(call_inst); + return NULL; + } + + //==- Shadow Frame -----------------------------------------------------==// + case IntrinsicHelper::AllocaShadowFrame: { + Expand_AllocaShadowFrame(call_inst.getArgOperand(0)); + return NULL; + } + case IntrinsicHelper::SetVReg: { + Expand_SetVReg(call_inst.getArgOperand(0), + call_inst.getArgOperand(1)); + return NULL; + } + case IntrinsicHelper::PopShadowFrame: { + Expand_PopShadowFrame(); + return NULL; + } + case IntrinsicHelper::UpdateDexPC: { + Expand_UpdateDexPC(call_inst.getArgOperand(0)); + return NULL; + } + + //==- Comparison -------------------------------------------------------==// + case IntrinsicHelper::CmplFloat: + case IntrinsicHelper::CmplDouble: { + return Expand_FPCompare(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + false); + } + case IntrinsicHelper::CmpgFloat: + case IntrinsicHelper::CmpgDouble: { + return Expand_FPCompare(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + true); + } + case IntrinsicHelper::CmpLong: { + return Expand_LongCompare(call_inst.getArgOperand(0), + call_inst.getArgOperand(1)); + } + + //==- Const ------------------------------------------------------------==// + case IntrinsicHelper::ConstInt: + case IntrinsicHelper::ConstLong: { + return call_inst.getArgOperand(0); + } + case IntrinsicHelper::ConstFloat: { + return irb_.CreateBitCast(call_inst.getArgOperand(0), + irb_.getJFloatTy()); + } + case IntrinsicHelper::ConstDouble: { + return irb_.CreateBitCast(call_inst.getArgOperand(0), + irb_.getJDoubleTy()); + } + case IntrinsicHelper::ConstObj: { + CHECK(LV2UInt(call_inst.getArgOperand(0)) == 0); + return irb_.getJNull(); + } + + //==- Method Info ------------------------------------------------------==// + case IntrinsicHelper::MethodInfo: { + // Nothing to be done, because MethodInfo carries optional hints that are + // not needed by the portable path. + return NULL; + } + + //==- Copy -------------------------------------------------------------==// + case IntrinsicHelper::CopyInt: + case IntrinsicHelper::CopyFloat: + case IntrinsicHelper::CopyLong: + case IntrinsicHelper::CopyDouble: + case IntrinsicHelper::CopyObj: { + return call_inst.getArgOperand(0); + } + + //==- Shift ------------------------------------------------------------==// + case IntrinsicHelper::SHLLong: { + return Expand_IntegerShift(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kIntegerSHL, kLong); + } + case IntrinsicHelper::SHRLong: { + return Expand_IntegerShift(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kIntegerSHR, kLong); + } + case IntrinsicHelper::USHRLong: { + return Expand_IntegerShift(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kIntegerUSHR, kLong); + } + case IntrinsicHelper::SHLInt: { + return Expand_IntegerShift(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kIntegerSHL, kInt); + } + case IntrinsicHelper::SHRInt: { + return Expand_IntegerShift(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kIntegerSHR, kInt); + } + case IntrinsicHelper::USHRInt: { + return Expand_IntegerShift(call_inst.getArgOperand(0), + call_inst.getArgOperand(1), + kIntegerUSHR, kInt); + } + + //==- Conversion -------------------------------------------------------==// + case IntrinsicHelper::IntToChar: { + return irb_.CreateZExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJCharTy()), + irb_.getJIntTy()); + } + case IntrinsicHelper::IntToShort: { + return irb_.CreateSExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJShortTy()), + irb_.getJIntTy()); + } + case IntrinsicHelper::IntToByte: { + return irb_.CreateSExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJByteTy()), + irb_.getJIntTy()); + } + + //==- Exception --------------------------------------------------------==// + case IntrinsicHelper::CatchTargets: { + UpdatePhiInstruction(current_bb_, irb_.GetInsertBlock()); + llvm::SwitchInst* si = llvm::dyn_cast<llvm::SwitchInst>(call_inst.getNextNode()); + CHECK(si != NULL); + irb_.CreateBr(si->getDefaultDest()); + si->eraseFromParent(); + return call_inst.getArgOperand(0); + } + + //==- Constructor barrier-----------------------------------------------==// + case IntrinsicHelper::ConstructorBarrier: { + irb_.CreateMemoryBarrier(art::kStoreStore); + return NULL; + } + + //==- Unknown Cases ----------------------------------------------------==// + case IntrinsicHelper::MaxIntrinsicId: + case IntrinsicHelper::UnknownId: + //default: + // NOTE: "default" is intentionally commented so that C/C++ compiler will + // give some warning on unmatched cases. + // NOTE: We should not implement these cases. + break; + } + UNIMPLEMENTED(FATAL) << "Unexpected GBC intrinsic: " << static_cast<int>(intr_id); + return NULL; +} + +} // anonymous namespace + +namespace art { +namespace llvm { + +::llvm::FunctionPass* +CreateGBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb, + CompilerDriver* driver, const DexCompilationUnit* dex_compilation_unit) { + return new GBCExpanderPass(intrinsic_helper, irb, driver, dex_compilation_unit); +} + +} // namespace llvm +} // namespace art |