diff options
Diffstat (limited to 'compiler/dex/portable/mir_to_gbc.cc')
-rw-r--r-- | compiler/dex/portable/mir_to_gbc.cc | 2056 |
1 files changed, 2056 insertions, 0 deletions
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc new file mode 100644 index 0000000..2be1ef4 --- /dev/null +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -0,0 +1,2056 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "object_utils.h" + +#include <llvm/ADT/DepthFirstIterator.h> +#include <llvm/Analysis/Verifier.h> +#include <llvm/Bitcode/ReaderWriter.h> +#include <llvm/IR/Instruction.h> +#include <llvm/IR/Instructions.h> +#include <llvm/IR/Metadata.h> +#include <llvm/IR/Type.h> +#include <llvm/Support/Casting.h> +#include <llvm/Support/InstIterator.h> +#include <llvm/Support/ToolOutputFile.h> + +#include "dex/compiler_internals.h" +#include "dex/dataflow_iterator-inl.h" +#include "dex/frontend.h" +#include "mir_to_gbc.h" + +#include "llvm/llvm_compilation_unit.h" +#include "llvm/utils_llvm.h" + +const char* kLabelFormat = "%c0x%x_%d"; +const char kInvalidBlock = 0xff; +const char kNormalBlock = 'L'; +const char kCatchBlock = 'C'; + +namespace art { + +::llvm::BasicBlock* MirConverter::GetLLVMBlock(int id) +{ + return id_to_block_map_.Get(id); +} + +::llvm::Value* MirConverter::GetLLVMValue(int s_reg) +{ + return llvm_values_.Get(s_reg); +} + +void MirConverter::SetVregOnValue(::llvm::Value* val, int s_reg) +{ + // Set vreg for debugging + art::llvm::IntrinsicHelper::IntrinsicId id = art::llvm::IntrinsicHelper::SetVReg; + ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id); + int v_reg = mir_graph_->SRegToVReg(s_reg); + ::llvm::Value* table_slot = irb_->getInt32(v_reg); + ::llvm::Value* args[] = { table_slot, val }; + irb_->CreateCall(func, args); +} + +// Replace the placeholder value with the real definition +void MirConverter::DefineValueOnly(::llvm::Value* val, int s_reg) +{ + ::llvm::Value* placeholder = GetLLVMValue(s_reg); + if (placeholder == NULL) { + // This can happen on instruction rewrite on verification failure + LOG(WARNING) << "Null placeholder"; + return; + } + placeholder->replaceAllUsesWith(val); + val->takeName(placeholder); + llvm_values_.Put(s_reg, val); + ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(placeholder); + DCHECK(inst != NULL); + inst->eraseFromParent(); + +} + +void MirConverter::DefineValue(::llvm::Value* val, int s_reg) +{ + DefineValueOnly(val, s_reg); + SetVregOnValue(val, s_reg); +} + +::llvm::Type* MirConverter::LlvmTypeFromLocRec(RegLocation loc) +{ + ::llvm::Type* res = NULL; + if (loc.wide) { + if (loc.fp) + res = irb_->getDoubleTy(); + else + res = irb_->getInt64Ty(); + } else { + if (loc.fp) { + res = irb_->getFloatTy(); + } else { + if (loc.ref) + res = irb_->getJObjectTy(); + else + res = irb_->getInt32Ty(); + } + } + return res; +} + +void MirConverter::InitIR() +{ + if (llvm_info_ == NULL) { + CompilerTls* tls = cu_->compiler_driver->GetTls(); + CHECK(tls != NULL); + llvm_info_ = static_cast<LLVMInfo*>(tls->GetLLVMInfo()); + if (llvm_info_ == NULL) { + llvm_info_ = new LLVMInfo(); + tls->SetLLVMInfo(llvm_info_); + } + } + context_ = llvm_info_->GetLLVMContext(); + module_ = llvm_info_->GetLLVMModule(); + intrinsic_helper_ = llvm_info_->GetIntrinsicHelper(); + irb_ = llvm_info_->GetIRBuilder(); +} + +::llvm::BasicBlock* MirConverter::FindCaseTarget(uint32_t vaddr) +{ + BasicBlock* bb = mir_graph_->FindBlock(vaddr); + DCHECK(bb != NULL); + return GetLLVMBlock(bb->id); +} + +void MirConverter::ConvertPackedSwitch(BasicBlock* bb, + int32_t table_offset, RegLocation rl_src) +{ + const Instruction::PackedSwitchPayload* payload = + reinterpret_cast<const Instruction::PackedSwitchPayload*>( + cu_->insns + current_dalvik_offset_ + table_offset); + + ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg); + + ::llvm::SwitchInst* sw = + irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id), + payload->case_count); + + for (uint16_t i = 0; i < payload->case_count; ++i) { + ::llvm::BasicBlock* llvm_bb = + FindCaseTarget(current_dalvik_offset_ + payload->targets[i]); + sw->addCase(irb_->getInt32(payload->first_key + i), llvm_bb); + } + ::llvm::MDNode* switch_node = + ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset)); + sw->setMetadata("SwitchTable", switch_node); + bb->taken = NULL; + bb->fall_through = NULL; +} + +void MirConverter::ConvertSparseSwitch(BasicBlock* bb, + int32_t table_offset, RegLocation rl_src) +{ + const Instruction::SparseSwitchPayload* payload = + reinterpret_cast<const Instruction::SparseSwitchPayload*>( + cu_->insns + current_dalvik_offset_ + table_offset); + + const int32_t* keys = payload->GetKeys(); + const int32_t* targets = payload->GetTargets(); + + ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg); + + ::llvm::SwitchInst* sw = + irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id), + payload->case_count); + + for (size_t i = 0; i < payload->case_count; ++i) { + ::llvm::BasicBlock* llvm_bb = + FindCaseTarget(current_dalvik_offset_ + targets[i]); + sw->addCase(irb_->getInt32(keys[i]), llvm_bb); + } + ::llvm::MDNode* switch_node = + ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset)); + sw->setMetadata("SwitchTable", switch_node); + bb->taken = NULL; + bb->fall_through = NULL; +} + +void MirConverter::ConvertSget(int32_t field_index, + art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest) +{ + ::llvm::Constant* field_idx = irb_->getInt32(field_index); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* res = irb_->CreateCall(intr, field_idx); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertSput(int32_t field_index, + art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src) +{ + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(irb_->getInt32(field_index)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + irb_->CreateCall(intr, args); +} + +void MirConverter::ConvertFillArrayData(int32_t offset, RegLocation rl_array) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + id = art::llvm::IntrinsicHelper::HLFillArrayData; + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(irb_->getInt32(offset)); + args.push_back(GetLLVMValue(rl_array.orig_sreg)); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + irb_->CreateCall(intr, args); +} + +::llvm::Value* MirConverter::EmitConst(::llvm::ArrayRef< ::llvm::Value*> src, + RegLocation loc) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + if (loc.wide) { + if (loc.fp) { + id = art::llvm::IntrinsicHelper::ConstDouble; + } else { + id = art::llvm::IntrinsicHelper::ConstLong; + } + } else { + if (loc.fp) { + id = art::llvm::IntrinsicHelper::ConstFloat; + } else if (loc.ref) { + id = art::llvm::IntrinsicHelper::ConstObj; + } else { + id = art::llvm::IntrinsicHelper::ConstInt; + } + } + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + return irb_->CreateCall(intr, src); +} + +void MirConverter::EmitPopShadowFrame() +{ + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction( + art::llvm::IntrinsicHelper::PopShadowFrame); + irb_->CreateCall(intr); +} + +::llvm::Value* MirConverter::EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src, + RegLocation loc) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + if (loc.wide) { + if (loc.fp) { + id = art::llvm::IntrinsicHelper::CopyDouble; + } else { + id = art::llvm::IntrinsicHelper::CopyLong; + } + } else { + if (loc.fp) { + id = art::llvm::IntrinsicHelper::CopyFloat; + } else if (loc.ref) { + id = art::llvm::IntrinsicHelper::CopyObj; + } else { + id = art::llvm::IntrinsicHelper::CopyInt; + } + } + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + return irb_->CreateCall(intr, src); +} + +void MirConverter::ConvertMoveException(RegLocation rl_dest) +{ + ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction( + art::llvm::IntrinsicHelper::GetException); + ::llvm::Value* res = irb_->CreateCall(func); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertThrow(RegLocation rl_src) +{ + ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg); + ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction( + art::llvm::IntrinsicHelper::HLThrowException); + irb_->CreateCall(func, src); +} + +void MirConverter::ConvertMonitorEnterExit(int opt_flags, + art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_src) +{ + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(irb_->getInt32(opt_flags)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id); + irb_->CreateCall(func, args); +} + +void MirConverter::ConvertArrayLength(int opt_flags, + RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(irb_->getInt32(opt_flags)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction( + art::llvm::IntrinsicHelper::OptArrayLength); + ::llvm::Value* res = irb_->CreateCall(func, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::EmitSuspendCheck() +{ + art::llvm::IntrinsicHelper::IntrinsicId id = + art::llvm::IntrinsicHelper::CheckSuspend; + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + irb_->CreateCall(intr); +} + +::llvm::Value* MirConverter::ConvertCompare(ConditionCode cc, + ::llvm::Value* src1, ::llvm::Value* src2) +{ + ::llvm::Value* res = NULL; + DCHECK_EQ(src1->getType(), src2->getType()); + switch(cc) { + case kCondEq: res = irb_->CreateICmpEQ(src1, src2); break; + case kCondNe: res = irb_->CreateICmpNE(src1, src2); break; + case kCondLt: res = irb_->CreateICmpSLT(src1, src2); break; + case kCondGe: res = irb_->CreateICmpSGE(src1, src2); break; + case kCondGt: res = irb_->CreateICmpSGT(src1, src2); break; + case kCondLe: res = irb_->CreateICmpSLE(src1, src2); break; + default: LOG(FATAL) << "Unexpected cc value " << cc; + } + return res; +} + +void MirConverter::ConvertCompareAndBranch(BasicBlock* bb, MIR* mir, + ConditionCode cc, RegLocation rl_src1, RegLocation rl_src2) +{ + if (bb->taken->start_offset <= mir->offset) { + EmitSuspendCheck(); + } + ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg); + ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg); + ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2); + cond_value->setName(StringPrintf("t%d", temp_name_++)); + irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id), + GetLLVMBlock(bb->fall_through->id)); + // Don't redo the fallthrough branch in the BB driver + bb->fall_through = NULL; +} + +void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb, + MIR* mir, ConditionCode cc, RegLocation rl_src1) +{ + if (bb->taken->start_offset <= mir->offset) { + EmitSuspendCheck(); + } + ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg); + ::llvm::Value* src2; + if (rl_src1.ref) { + src2 = irb_->getJNull(); + } else { + src2 = irb_->getInt32(0); + } + ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2); + irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id), + GetLLVMBlock(bb->fall_through->id)); + // Don't redo the fallthrough branch in the BB driver + bb->fall_through = NULL; +} + +::llvm::Value* MirConverter::GenDivModOp(bool is_div, bool is_long, + ::llvm::Value* src1, ::llvm::Value* src2) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + if (is_long) { + if (is_div) { + id = art::llvm::IntrinsicHelper::DivLong; + } else { + id = art::llvm::IntrinsicHelper::RemLong; + } + } else { + if (is_div) { + id = art::llvm::IntrinsicHelper::DivInt; + } else { + id = art::llvm::IntrinsicHelper::RemInt; + } + } + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::SmallVector< ::llvm::Value*, 2>args; + args.push_back(src1); + args.push_back(src2); + return irb_->CreateCall(intr, args); +} + +::llvm::Value* MirConverter::GenArithOp(OpKind op, bool is_long, + ::llvm::Value* src1, ::llvm::Value* src2) +{ + ::llvm::Value* res = NULL; + switch(op) { + case kOpAdd: res = irb_->CreateAdd(src1, src2); break; + case kOpSub: res = irb_->CreateSub(src1, src2); break; + case kOpRsub: res = irb_->CreateSub(src2, src1); break; + case kOpMul: res = irb_->CreateMul(src1, src2); break; + case kOpOr: res = irb_->CreateOr(src1, src2); break; + case kOpAnd: res = irb_->CreateAnd(src1, src2); break; + case kOpXor: res = irb_->CreateXor(src1, src2); break; + case kOpDiv: res = GenDivModOp(true, is_long, src1, src2); break; + case kOpRem: res = GenDivModOp(false, is_long, src1, src2); break; + case kOpLsl: res = irb_->CreateShl(src1, src2); break; + case kOpLsr: res = irb_->CreateLShr(src1, src2); break; + case kOpAsr: res = irb_->CreateAShr(src1, src2); break; + default: + LOG(FATAL) << "Invalid op " << op; + } + return res; +} + +void MirConverter::ConvertFPArithOp(OpKind op, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) +{ + ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg); + ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg); + ::llvm::Value* res = NULL; + switch(op) { + case kOpAdd: res = irb_->CreateFAdd(src1, src2); break; + case kOpSub: res = irb_->CreateFSub(src1, src2); break; + case kOpMul: res = irb_->CreateFMul(src1, src2); break; + case kOpDiv: res = irb_->CreateFDiv(src1, src2); break; + case kOpRem: res = irb_->CreateFRem(src1, src2); break; + default: + LOG(FATAL) << "Invalid op " << op; + } + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) +{ + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::SmallVector< ::llvm::Value*, 2>args; + args.push_back(GetLLVMValue(rl_src1.orig_sreg)); + args.push_back(GetLLVMValue(rl_src2.orig_sreg)); + ::llvm::Value* res = irb_->CreateCall(intr, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_dest, RegLocation rl_src, int shift_amount) +{ + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::SmallVector< ::llvm::Value*, 2>args; + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + args.push_back(irb_->getInt32(shift_amount)); + ::llvm::Value* res = irb_->CreateCall(intr, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertArithOp(OpKind op, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) +{ + ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg); + ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg); + DCHECK_EQ(src1->getType(), src2->getType()); + ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertArithOpLit(OpKind op, RegLocation rl_dest, + RegLocation rl_src1, int32_t imm) +{ + ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg); + ::llvm::Value* src2 = irb_->getInt32(imm); + ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2); + DefineValue(res, rl_dest.orig_sreg); +} + +/* + * Process arguments for invoke. Note: this code is also used to + * collect and process arguments for NEW_FILLED_ARRAY and NEW_FILLED_ARRAY_RANGE. + * The requirements are similar. + */ +void MirConverter::ConvertInvoke(BasicBlock* bb, MIR* mir, + InvokeType invoke_type, bool is_range, bool is_filled_new_array) +{ + CallInfo* info = mir_graph_->NewMemCallInfo(bb, mir, invoke_type, is_range); + ::llvm::SmallVector< ::llvm::Value*, 10> args; + // Insert the invoke_type + args.push_back(irb_->getInt32(static_cast<int>(invoke_type))); + // Insert the method_idx + args.push_back(irb_->getInt32(info->index)); + // Insert the optimization flags + args.push_back(irb_->getInt32(info->opt_flags)); + // Now, insert the actual arguments + for (int i = 0; i < info->num_arg_words;) { + ::llvm::Value* val = GetLLVMValue(info->args[i].orig_sreg); + args.push_back(val); + i += info->args[i].wide ? 2 : 1; + } + /* + * Choose the invoke return type based on actual usage. Note: may + * be different than shorty. For example, if a function return value + * is not used, we'll treat this as a void invoke. + */ + art::llvm::IntrinsicHelper::IntrinsicId id; + if (is_filled_new_array) { + id = art::llvm::IntrinsicHelper::HLFilledNewArray; + } else if (info->result.location == kLocInvalid) { + id = art::llvm::IntrinsicHelper::HLInvokeVoid; + } else { + if (info->result.wide) { + if (info->result.fp) { + id = art::llvm::IntrinsicHelper::HLInvokeDouble; + } else { + id = art::llvm::IntrinsicHelper::HLInvokeLong; + } + } else if (info->result.ref) { + id = art::llvm::IntrinsicHelper::HLInvokeObj; + } else if (info->result.fp) { + id = art::llvm::IntrinsicHelper::HLInvokeFloat; + } else { + id = art::llvm::IntrinsicHelper::HLInvokeInt; + } + } + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* res = irb_->CreateCall(intr, args); + if (info->result.location != kLocInvalid) { + DefineValue(res, info->result.orig_sreg); + } +} + +void MirConverter::ConvertConstObject(uint32_t idx, + art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest) +{ + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* index = irb_->getInt32(idx); + ::llvm::Value* res = irb_->CreateCall(intr, index); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertCheckCast(uint32_t type_idx, RegLocation rl_src) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + id = art::llvm::IntrinsicHelper::HLCheckCast; + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(irb_->getInt32(type_idx)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + irb_->CreateCall(intr, args); +} + +void MirConverter::ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + id = art::llvm::IntrinsicHelper::NewInstance; + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* index = irb_->getInt32(type_idx); + ::llvm::Value* res = irb_->CreateCall(intr, index); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertNewArray(uint32_t type_idx, + RegLocation rl_dest, RegLocation rl_src) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + id = art::llvm::IntrinsicHelper::NewArray; + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(irb_->getInt32(type_idx)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + ::llvm::Value* res = irb_->CreateCall(intr, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertAget(int opt_flags, + art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index) +{ + ::llvm::SmallVector< ::llvm::Value*, 3> args; + args.push_back(irb_->getInt32(opt_flags)); + args.push_back(GetLLVMValue(rl_array.orig_sreg)); + args.push_back(GetLLVMValue(rl_index.orig_sreg)); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* res = irb_->CreateCall(intr, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertAput(int opt_flags, + art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_src, RegLocation rl_array, RegLocation rl_index) +{ + ::llvm::SmallVector< ::llvm::Value*, 4> args; + args.push_back(irb_->getInt32(opt_flags)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + args.push_back(GetLLVMValue(rl_array.orig_sreg)); + args.push_back(GetLLVMValue(rl_index.orig_sreg)); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + irb_->CreateCall(intr, args); +} + +void MirConverter::ConvertIget(int opt_flags, + art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_dest, RegLocation rl_obj, int field_index) +{ + ::llvm::SmallVector< ::llvm::Value*, 3> args; + args.push_back(irb_->getInt32(opt_flags)); + args.push_back(GetLLVMValue(rl_obj.orig_sreg)); + args.push_back(irb_->getInt32(field_index)); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* res = irb_->CreateCall(intr, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertIput(int opt_flags, + art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_src, RegLocation rl_obj, int field_index) +{ + ::llvm::SmallVector< ::llvm::Value*, 4> args; + args.push_back(irb_->getInt32(opt_flags)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + args.push_back(GetLLVMValue(rl_obj.orig_sreg)); + args.push_back(irb_->getInt32(field_index)); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + irb_->CreateCall(intr, args); +} + +void MirConverter::ConvertInstanceOf(uint32_t type_idx, + RegLocation rl_dest, RegLocation rl_src) +{ + art::llvm::IntrinsicHelper::IntrinsicId id; + id = art::llvm::IntrinsicHelper::InstanceOf; + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(irb_->getInt32(type_idx)); + args.push_back(GetLLVMValue(rl_src.orig_sreg)); + ::llvm::Value* res = irb_->CreateCall(intr, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::Value* res = irb_->CreateSExt(GetLLVMValue(rl_src.orig_sreg), + irb_->getInt64Ty()); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg); + ::llvm::Value* res = irb_->CreateTrunc(src, irb_->getInt32Ty()); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg); + ::llvm::Value* res = irb_->CreateFPExt(src, irb_->getDoubleTy()); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg); + ::llvm::Value* res = irb_->CreateFPTrunc(src, irb_->getFloatTy()); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) +{ + DCHECK_EQ(rl_src1.fp, rl_src2.fp); + DCHECK_EQ(rl_src1.wide, rl_src2.wide); + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::SmallVector< ::llvm::Value*, 2> args; + args.push_back(GetLLVMValue(rl_src1.orig_sreg)); + args.push_back(GetLLVMValue(rl_src2.orig_sreg)); + ::llvm::Value* res = irb_->CreateCall(intr, args); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src, + art::llvm::IntrinsicHelper::IntrinsicId id) +{ + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* res = + irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg)); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertNeg(RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::Value* res = irb_->CreateNeg(GetLLVMValue(rl_src.orig_sreg)); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest, + RegLocation rl_src) +{ + ::llvm::Value* res = + irb_->CreateSIToFP(GetLLVMValue(rl_src.orig_sreg), ty); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id, + RegLocation rl_dest, + RegLocation rl_src) +{ + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* res = irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg)); + DefineValue(res, rl_dest.orig_sreg); +} + + +void MirConverter::ConvertNegFP(RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::Value* res = + irb_->CreateFNeg(GetLLVMValue(rl_src.orig_sreg)); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::ConvertNot(RegLocation rl_dest, RegLocation rl_src) +{ + ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg); + ::llvm::Value* res = irb_->CreateXor(src, static_cast<uint64_t>(-1)); + DefineValue(res, rl_dest.orig_sreg); +} + +void MirConverter::EmitConstructorBarrier() { + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction( + art::llvm::IntrinsicHelper::ConstructorBarrier); + irb_->CreateCall(intr); +} + +/* + * Target-independent code generation. Use only high-level + * load/store utilities here, or target-dependent genXX() handlers + * when necessary. + */ +bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb, + ::llvm::BasicBlock* llvm_bb) +{ + bool res = false; // Assume success + RegLocation rl_src[3]; + RegLocation rl_dest = mir_graph_->GetBadLoc(); + Instruction::Code opcode = mir->dalvikInsn.opcode; + int op_val = opcode; + uint32_t vB = mir->dalvikInsn.vB; + uint32_t vC = mir->dalvikInsn.vC; + int opt_flags = mir->optimization_flags; + + if (cu_->verbose) { + if (op_val < kMirOpFirst) { + LOG(INFO) << ".. " << Instruction::Name(opcode) << " 0x" << std::hex << op_val; + } else { + LOG(INFO) << mir_graph_->extended_mir_op_names_[op_val - kMirOpFirst] << " 0x" << std::hex << op_val; + } + } + + /* Prep Src and Dest locations */ + int next_sreg = 0; + int next_loc = 0; + int attrs = mir_graph_->oat_data_flow_attributes_[opcode]; + rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc(); + if (attrs & DF_UA) { + if (attrs & DF_A_WIDE) { + rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg); + next_sreg+= 2; + } else { + rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg); + next_sreg++; + } + } + if (attrs & DF_UB) { + if (attrs & DF_B_WIDE) { + rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg); + next_sreg+= 2; + } else { + rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg); + next_sreg++; + } + } + if (attrs & DF_UC) { + if (attrs & DF_C_WIDE) { + rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg); + } else { + rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg); + } + } + if (attrs & DF_DA) { + if (attrs & DF_A_WIDE) { + rl_dest = mir_graph_->GetDestWide(mir); + } else { + rl_dest = mir_graph_->GetDest(mir); + } + } + + switch (opcode) { + case Instruction::NOP: + break; + + case Instruction::MOVE: + case Instruction::MOVE_OBJECT: + case Instruction::MOVE_16: + case Instruction::MOVE_OBJECT_16: + case Instruction::MOVE_OBJECT_FROM16: + case Instruction::MOVE_FROM16: + case Instruction::MOVE_WIDE: + case Instruction::MOVE_WIDE_16: + case Instruction::MOVE_WIDE_FROM16: { + /* + * Moves/copies are meaningless in pure SSA register form, + * but we need to preserve them for the conversion back into + * MIR (at least until we stop using the Dalvik register maps). + * Insert a dummy intrinsic copy call, which will be recognized + * by the quick path and removed by the portable path. + */ + ::llvm::Value* src = GetLLVMValue(rl_src[0].orig_sreg); + ::llvm::Value* res = EmitCopy(src, rl_dest); + DefineValue(res, rl_dest.orig_sreg); + } + break; + + case Instruction::CONST: + case Instruction::CONST_4: + case Instruction::CONST_16: { + ::llvm::Constant* imm_value = irb_->getJInt(vB); + ::llvm::Value* res = EmitConst(imm_value, rl_dest); + DefineValue(res, rl_dest.orig_sreg); + } + break; + + case Instruction::CONST_WIDE_16: + case Instruction::CONST_WIDE_32: { + // Sign extend to 64 bits + int64_t imm = static_cast<int32_t>(vB); + ::llvm::Constant* imm_value = irb_->getJLong(imm); + ::llvm::Value* res = EmitConst(imm_value, rl_dest); + DefineValue(res, rl_dest.orig_sreg); + } + break; + + case Instruction::CONST_HIGH16: { + ::llvm::Constant* imm_value = irb_->getJInt(vB << 16); + ::llvm::Value* res = EmitConst(imm_value, rl_dest); + DefineValue(res, rl_dest.orig_sreg); + } + break; + + case Instruction::CONST_WIDE: { + ::llvm::Constant* imm_value = + irb_->getJLong(mir->dalvikInsn.vB_wide); + ::llvm::Value* res = EmitConst(imm_value, rl_dest); + DefineValue(res, rl_dest.orig_sreg); + } + break; + case Instruction::CONST_WIDE_HIGH16: { + int64_t imm = static_cast<int64_t>(vB) << 48; + ::llvm::Constant* imm_value = irb_->getJLong(imm); + ::llvm::Value* res = EmitConst(imm_value, rl_dest); + DefineValue(res, rl_dest.orig_sreg); + } + break; + + case Instruction::SPUT_OBJECT: + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputObject, + rl_src[0]); + break; + case Instruction::SPUT: + if (rl_src[0].fp) { + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputFloat, + rl_src[0]); + } else { + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSput, rl_src[0]); + } + break; + case Instruction::SPUT_BOOLEAN: + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputBoolean, + rl_src[0]); + break; + case Instruction::SPUT_BYTE: + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputByte, rl_src[0]); + break; + case Instruction::SPUT_CHAR: + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputChar, rl_src[0]); + break; + case Instruction::SPUT_SHORT: + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputShort, rl_src[0]); + break; + case Instruction::SPUT_WIDE: + if (rl_src[0].fp) { + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputDouble, + rl_src[0]); + } else { + ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputWide, + rl_src[0]); + } + break; + + case Instruction::SGET_OBJECT: + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetObject, rl_dest); + break; + case Instruction::SGET: + if (rl_dest.fp) { + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetFloat, rl_dest); + } else { + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSget, rl_dest); + } + break; + case Instruction::SGET_BOOLEAN: + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetBoolean, rl_dest); + break; + case Instruction::SGET_BYTE: + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetByte, rl_dest); + break; + case Instruction::SGET_CHAR: + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetChar, rl_dest); + break; + case Instruction::SGET_SHORT: + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetShort, rl_dest); + break; + case Instruction::SGET_WIDE: + if (rl_dest.fp) { + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetDouble, + rl_dest); + } else { + ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetWide, rl_dest); + } + break; + + case Instruction::RETURN_WIDE: + case Instruction::RETURN: + case Instruction::RETURN_OBJECT: { + if (!mir_graph_->MethodIsLeaf()) { + EmitSuspendCheck(); + } + EmitPopShadowFrame(); + irb_->CreateRet(GetLLVMValue(rl_src[0].orig_sreg)); + DCHECK(bb->terminated_by_return); + } + break; + + case Instruction::RETURN_VOID: { + if (((cu_->access_flags & kAccConstructor) != 0) && + cu_->compiler_driver->RequiresConstructorBarrier(Thread::Current(), + cu_->dex_file, + cu_->class_def_idx)) { + EmitConstructorBarrier(); + } + if (!mir_graph_->MethodIsLeaf()) { + EmitSuspendCheck(); + } + EmitPopShadowFrame(); + irb_->CreateRetVoid(); + DCHECK(bb->terminated_by_return); + } + break; + + case Instruction::IF_EQ: + ConvertCompareAndBranch(bb, mir, kCondEq, rl_src[0], rl_src[1]); + break; + case Instruction::IF_NE: + ConvertCompareAndBranch(bb, mir, kCondNe, rl_src[0], rl_src[1]); + break; + case Instruction::IF_LT: + ConvertCompareAndBranch(bb, mir, kCondLt, rl_src[0], rl_src[1]); + break; + case Instruction::IF_GE: + ConvertCompareAndBranch(bb, mir, kCondGe, rl_src[0], rl_src[1]); + break; + case Instruction::IF_GT: + ConvertCompareAndBranch(bb, mir, kCondGt, rl_src[0], rl_src[1]); + break; + case Instruction::IF_LE: + ConvertCompareAndBranch(bb, mir, kCondLe, rl_src[0], rl_src[1]); + break; + case Instruction::IF_EQZ: + ConvertCompareZeroAndBranch(bb, mir, kCondEq, rl_src[0]); + break; + case Instruction::IF_NEZ: + ConvertCompareZeroAndBranch(bb, mir, kCondNe, rl_src[0]); + break; + case Instruction::IF_LTZ: + ConvertCompareZeroAndBranch(bb, mir, kCondLt, rl_src[0]); + break; + case Instruction::IF_GEZ: + ConvertCompareZeroAndBranch(bb, mir, kCondGe, rl_src[0]); + break; + case Instruction::IF_GTZ: + ConvertCompareZeroAndBranch(bb, mir, kCondGt, rl_src[0]); + break; + case Instruction::IF_LEZ: + ConvertCompareZeroAndBranch(bb, mir, kCondLe, rl_src[0]); + break; + + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: { + if (bb->taken->start_offset <= bb->start_offset) { + EmitSuspendCheck(); + } + irb_->CreateBr(GetLLVMBlock(bb->taken->id)); + } + break; + + case Instruction::ADD_LONG: + case Instruction::ADD_LONG_2ADDR: + case Instruction::ADD_INT: + case Instruction::ADD_INT_2ADDR: + ConvertArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::SUB_LONG: + case Instruction::SUB_LONG_2ADDR: + case Instruction::SUB_INT: + case Instruction::SUB_INT_2ADDR: + ConvertArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::MUL_LONG: + case Instruction::MUL_LONG_2ADDR: + case Instruction::MUL_INT: + case Instruction::MUL_INT_2ADDR: + ConvertArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::DIV_LONG: + case Instruction::DIV_LONG_2ADDR: + case Instruction::DIV_INT: + case Instruction::DIV_INT_2ADDR: + ConvertArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::REM_LONG: + case Instruction::REM_LONG_2ADDR: + case Instruction::REM_INT: + case Instruction::REM_INT_2ADDR: + ConvertArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::AND_LONG: + case Instruction::AND_LONG_2ADDR: + case Instruction::AND_INT: + case Instruction::AND_INT_2ADDR: + ConvertArithOp(kOpAnd, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::OR_LONG: + case Instruction::OR_LONG_2ADDR: + case Instruction::OR_INT: + case Instruction::OR_INT_2ADDR: + ConvertArithOp(kOpOr, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::XOR_LONG: + case Instruction::XOR_LONG_2ADDR: + case Instruction::XOR_INT: + case Instruction::XOR_INT_2ADDR: + ConvertArithOp(kOpXor, rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::SHL_LONG: + case Instruction::SHL_LONG_2ADDR: + ConvertShift(art::llvm::IntrinsicHelper::SHLLong, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::SHL_INT: + case Instruction::SHL_INT_2ADDR: + ConvertShift(art::llvm::IntrinsicHelper::SHLInt, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::SHR_LONG: + case Instruction::SHR_LONG_2ADDR: + ConvertShift(art::llvm::IntrinsicHelper::SHRLong, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::SHR_INT: + case Instruction::SHR_INT_2ADDR: + ConvertShift(art::llvm::IntrinsicHelper::SHRInt, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::USHR_LONG: + case Instruction::USHR_LONG_2ADDR: + ConvertShift(art::llvm::IntrinsicHelper::USHRLong, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::USHR_INT: + case Instruction::USHR_INT_2ADDR: + ConvertShift(art::llvm::IntrinsicHelper::USHRInt, + rl_dest, rl_src[0], rl_src[1]); + break; + + case Instruction::ADD_INT_LIT16: + case Instruction::ADD_INT_LIT8: + ConvertArithOpLit(kOpAdd, rl_dest, rl_src[0], vC); + break; + case Instruction::RSUB_INT: + case Instruction::RSUB_INT_LIT8: + ConvertArithOpLit(kOpRsub, rl_dest, rl_src[0], vC); + break; + case Instruction::MUL_INT_LIT16: + case Instruction::MUL_INT_LIT8: + ConvertArithOpLit(kOpMul, rl_dest, rl_src[0], vC); + break; + case Instruction::DIV_INT_LIT16: + case Instruction::DIV_INT_LIT8: + ConvertArithOpLit(kOpDiv, rl_dest, rl_src[0], vC); + break; + case Instruction::REM_INT_LIT16: + case Instruction::REM_INT_LIT8: + ConvertArithOpLit(kOpRem, rl_dest, rl_src[0], vC); + break; + case Instruction::AND_INT_LIT16: + case Instruction::AND_INT_LIT8: + ConvertArithOpLit(kOpAnd, rl_dest, rl_src[0], vC); + break; + case Instruction::OR_INT_LIT16: + case Instruction::OR_INT_LIT8: + ConvertArithOpLit(kOpOr, rl_dest, rl_src[0], vC); + break; + case Instruction::XOR_INT_LIT16: + case Instruction::XOR_INT_LIT8: + ConvertArithOpLit(kOpXor, rl_dest, rl_src[0], vC); + break; + case Instruction::SHL_INT_LIT8: + ConvertShiftLit(art::llvm::IntrinsicHelper::SHLInt, + rl_dest, rl_src[0], vC & 0x1f); + break; + case Instruction::SHR_INT_LIT8: + ConvertShiftLit(art::llvm::IntrinsicHelper::SHRInt, + rl_dest, rl_src[0], vC & 0x1f); + break; + case Instruction::USHR_INT_LIT8: + ConvertShiftLit(art::llvm::IntrinsicHelper::USHRInt, + rl_dest, rl_src[0], vC & 0x1f); + break; + + case Instruction::ADD_FLOAT: + case Instruction::ADD_FLOAT_2ADDR: + case Instruction::ADD_DOUBLE: + case Instruction::ADD_DOUBLE_2ADDR: + ConvertFPArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]); + break; + + case Instruction::SUB_FLOAT: + case Instruction::SUB_FLOAT_2ADDR: + case Instruction::SUB_DOUBLE: + case Instruction::SUB_DOUBLE_2ADDR: + ConvertFPArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]); + break; + + case Instruction::MUL_FLOAT: + case Instruction::MUL_FLOAT_2ADDR: + case Instruction::MUL_DOUBLE: + case Instruction::MUL_DOUBLE_2ADDR: + ConvertFPArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]); + break; + + case Instruction::DIV_FLOAT: + case Instruction::DIV_FLOAT_2ADDR: + case Instruction::DIV_DOUBLE: + case Instruction::DIV_DOUBLE_2ADDR: + ConvertFPArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]); + break; + + case Instruction::REM_FLOAT: + case Instruction::REM_FLOAT_2ADDR: + case Instruction::REM_DOUBLE: + case Instruction::REM_DOUBLE_2ADDR: + ConvertFPArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]); + break; + + case Instruction::INVOKE_STATIC: + ConvertInvoke(bb, mir, kStatic, false /*range*/, + false /* NewFilledArray */); + break; + case Instruction::INVOKE_STATIC_RANGE: + ConvertInvoke(bb, mir, kStatic, true /*range*/, + false /* NewFilledArray */); + break; + + case Instruction::INVOKE_DIRECT: + ConvertInvoke(bb, mir, kDirect, false /*range*/, + false /* NewFilledArray */); + break; + case Instruction::INVOKE_DIRECT_RANGE: + ConvertInvoke(bb, mir, kDirect, true /*range*/, + false /* NewFilledArray */); + break; + + case Instruction::INVOKE_VIRTUAL: + ConvertInvoke(bb, mir, kVirtual, false /*range*/, + false /* NewFilledArray */); + break; + case Instruction::INVOKE_VIRTUAL_RANGE: + ConvertInvoke(bb, mir, kVirtual, true /*range*/, + false /* NewFilledArray */); + break; + + case Instruction::INVOKE_SUPER: + ConvertInvoke(bb, mir, kSuper, false /*range*/, + false /* NewFilledArray */); + break; + case Instruction::INVOKE_SUPER_RANGE: + ConvertInvoke(bb, mir, kSuper, true /*range*/, + false /* NewFilledArray */); + break; + + case Instruction::INVOKE_INTERFACE: + ConvertInvoke(bb, mir, kInterface, false /*range*/, + false /* NewFilledArray */); + break; + case Instruction::INVOKE_INTERFACE_RANGE: + ConvertInvoke(bb, mir, kInterface, true /*range*/, + false /* NewFilledArray */); + break; + case Instruction::FILLED_NEW_ARRAY: + ConvertInvoke(bb, mir, kInterface, false /*range*/, + true /* NewFilledArray */); + break; + case Instruction::FILLED_NEW_ARRAY_RANGE: + ConvertInvoke(bb, mir, kInterface, true /*range*/, + true /* NewFilledArray */); + break; + + case Instruction::CONST_STRING: + case Instruction::CONST_STRING_JUMBO: + ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstString, + rl_dest); + break; + + case Instruction::CONST_CLASS: + ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstClass, + rl_dest); + break; + + case Instruction::CHECK_CAST: + ConvertCheckCast(vB, rl_src[0]); + break; + + case Instruction::NEW_INSTANCE: + ConvertNewInstance(vB, rl_dest); + break; + + case Instruction::MOVE_EXCEPTION: + ConvertMoveException(rl_dest); + break; + + case Instruction::THROW: + ConvertThrow(rl_src[0]); + /* + * If this throw is standalone, terminate. + * If it might rethrow, force termination + * of the following block. + */ + if (bb->fall_through == NULL) { + irb_->CreateUnreachable(); + } else { + bb->fall_through->fall_through = NULL; + bb->fall_through->taken = NULL; + } + break; + + case Instruction::MOVE_RESULT_WIDE: + case Instruction::MOVE_RESULT: + case Instruction::MOVE_RESULT_OBJECT: + /* + * All move_results should have been folded into the preceeding invoke. + */ + LOG(FATAL) << "Unexpected move_result"; + break; + + case Instruction::MONITOR_ENTER: + ConvertMonitorEnterExit(opt_flags, + art::llvm::IntrinsicHelper::MonitorEnter, + rl_src[0]); + break; + + case Instruction::MONITOR_EXIT: + ConvertMonitorEnterExit(opt_flags, + art::llvm::IntrinsicHelper::MonitorExit, + rl_src[0]); + break; + + case Instruction::ARRAY_LENGTH: + ConvertArrayLength(opt_flags, rl_dest, rl_src[0]); + break; + + case Instruction::NEW_ARRAY: + ConvertNewArray(vC, rl_dest, rl_src[0]); + break; + + case Instruction::INSTANCE_OF: + ConvertInstanceOf(vC, rl_dest, rl_src[0]); + break; + + case Instruction::AGET: + if (rl_dest.fp) { + ConvertAget(opt_flags, + art::llvm::IntrinsicHelper::HLArrayGetFloat, + rl_dest, rl_src[0], rl_src[1]); + } else { + ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGet, + rl_dest, rl_src[0], rl_src[1]); + } + break; + case Instruction::AGET_OBJECT: + ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetObject, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::AGET_BOOLEAN: + ConvertAget(opt_flags, + art::llvm::IntrinsicHelper::HLArrayGetBoolean, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::AGET_BYTE: + ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetByte, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::AGET_CHAR: + ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetChar, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::AGET_SHORT: + ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetShort, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::AGET_WIDE: + if (rl_dest.fp) { + ConvertAget(opt_flags, + art::llvm::IntrinsicHelper::HLArrayGetDouble, + rl_dest, rl_src[0], rl_src[1]); + } else { + ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetWide, + rl_dest, rl_src[0], rl_src[1]); + } + break; + + case Instruction::APUT: + if (rl_src[0].fp) { + ConvertAput(opt_flags, + art::llvm::IntrinsicHelper::HLArrayPutFloat, + rl_src[0], rl_src[1], rl_src[2]); + } else { + ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPut, + rl_src[0], rl_src[1], rl_src[2]); + } + break; + case Instruction::APUT_OBJECT: + ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutObject, + rl_src[0], rl_src[1], rl_src[2]); + break; + case Instruction::APUT_BOOLEAN: + ConvertAput(opt_flags, + art::llvm::IntrinsicHelper::HLArrayPutBoolean, + rl_src[0], rl_src[1], rl_src[2]); + break; + case Instruction::APUT_BYTE: + ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutByte, + rl_src[0], rl_src[1], rl_src[2]); + break; + case Instruction::APUT_CHAR: + ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutChar, + rl_src[0], rl_src[1], rl_src[2]); + break; + case Instruction::APUT_SHORT: + ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutShort, + rl_src[0], rl_src[1], rl_src[2]); + break; + case Instruction::APUT_WIDE: + if (rl_src[0].fp) { + ConvertAput(opt_flags, + art::llvm::IntrinsicHelper::HLArrayPutDouble, + rl_src[0], rl_src[1], rl_src[2]); + } else { + ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutWide, + rl_src[0], rl_src[1], rl_src[2]); + } + break; + + case Instruction::IGET: + if (rl_dest.fp) { + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetFloat, + rl_dest, rl_src[0], vC); + } else { + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGet, + rl_dest, rl_src[0], vC); + } + break; + case Instruction::IGET_OBJECT: + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetObject, + rl_dest, rl_src[0], vC); + break; + case Instruction::IGET_BOOLEAN: + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetBoolean, + rl_dest, rl_src[0], vC); + break; + case Instruction::IGET_BYTE: + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetByte, + rl_dest, rl_src[0], vC); + break; + case Instruction::IGET_CHAR: + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetChar, + rl_dest, rl_src[0], vC); + break; + case Instruction::IGET_SHORT: + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetShort, + rl_dest, rl_src[0], vC); + break; + case Instruction::IGET_WIDE: + if (rl_dest.fp) { + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetDouble, + rl_dest, rl_src[0], vC); + } else { + ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetWide, + rl_dest, rl_src[0], vC); + } + break; + case Instruction::IPUT: + if (rl_src[0].fp) { + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutFloat, + rl_src[0], rl_src[1], vC); + } else { + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPut, + rl_src[0], rl_src[1], vC); + } + break; + case Instruction::IPUT_OBJECT: + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutObject, + rl_src[0], rl_src[1], vC); + break; + case Instruction::IPUT_BOOLEAN: + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutBoolean, + rl_src[0], rl_src[1], vC); + break; + case Instruction::IPUT_BYTE: + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutByte, + rl_src[0], rl_src[1], vC); + break; + case Instruction::IPUT_CHAR: + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutChar, + rl_src[0], rl_src[1], vC); + break; + case Instruction::IPUT_SHORT: + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutShort, + rl_src[0], rl_src[1], vC); + break; + case Instruction::IPUT_WIDE: + if (rl_src[0].fp) { + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutDouble, + rl_src[0], rl_src[1], vC); + } else { + ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutWide, + rl_src[0], rl_src[1], vC); + } + break; + + case Instruction::FILL_ARRAY_DATA: + ConvertFillArrayData(vB, rl_src[0]); + break; + + case Instruction::LONG_TO_INT: + ConvertLongToInt(rl_dest, rl_src[0]); + break; + + case Instruction::INT_TO_LONG: + ConvertIntToLong(rl_dest, rl_src[0]); + break; + + case Instruction::INT_TO_CHAR: + ConvertIntNarrowing(rl_dest, rl_src[0], + art::llvm::IntrinsicHelper::IntToChar); + break; + case Instruction::INT_TO_BYTE: + ConvertIntNarrowing(rl_dest, rl_src[0], + art::llvm::IntrinsicHelper::IntToByte); + break; + case Instruction::INT_TO_SHORT: + ConvertIntNarrowing(rl_dest, rl_src[0], + art::llvm::IntrinsicHelper::IntToShort); + break; + + case Instruction::INT_TO_FLOAT: + case Instruction::LONG_TO_FLOAT: + ConvertIntToFP(irb_->getFloatTy(), rl_dest, rl_src[0]); + break; + + case Instruction::INT_TO_DOUBLE: + case Instruction::LONG_TO_DOUBLE: + ConvertIntToFP(irb_->getDoubleTy(), rl_dest, rl_src[0]); + break; + + case Instruction::FLOAT_TO_DOUBLE: + ConvertFloatToDouble(rl_dest, rl_src[0]); + break; + + case Instruction::DOUBLE_TO_FLOAT: + ConvertDoubleToFloat(rl_dest, rl_src[0]); + break; + + case Instruction::NEG_LONG: + case Instruction::NEG_INT: + ConvertNeg(rl_dest, rl_src[0]); + break; + + case Instruction::NEG_FLOAT: + case Instruction::NEG_DOUBLE: + ConvertNegFP(rl_dest, rl_src[0]); + break; + + case Instruction::NOT_LONG: + case Instruction::NOT_INT: + ConvertNot(rl_dest, rl_src[0]); + break; + + case Instruction::FLOAT_TO_INT: + ConvertFPToInt(art::llvm::IntrinsicHelper::F2I, rl_dest, rl_src[0]); + break; + + case Instruction::DOUBLE_TO_INT: + ConvertFPToInt(art::llvm::IntrinsicHelper::D2I, rl_dest, rl_src[0]); + break; + + case Instruction::FLOAT_TO_LONG: + ConvertFPToInt(art::llvm::IntrinsicHelper::F2L, rl_dest, rl_src[0]); + break; + + case Instruction::DOUBLE_TO_LONG: + ConvertFPToInt(art::llvm::IntrinsicHelper::D2L, rl_dest, rl_src[0]); + break; + + case Instruction::CMPL_FLOAT: + ConvertWideComparison(art::llvm::IntrinsicHelper::CmplFloat, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::CMPG_FLOAT: + ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgFloat, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::CMPL_DOUBLE: + ConvertWideComparison(art::llvm::IntrinsicHelper::CmplDouble, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::CMPG_DOUBLE: + ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgDouble, + rl_dest, rl_src[0], rl_src[1]); + break; + case Instruction::CMP_LONG: + ConvertWideComparison(art::llvm::IntrinsicHelper::CmpLong, + rl_dest, rl_src[0], rl_src[1]); + break; + + case Instruction::PACKED_SWITCH: + ConvertPackedSwitch(bb, vB, rl_src[0]); + break; + + case Instruction::SPARSE_SWITCH: + ConvertSparseSwitch(bb, vB, rl_src[0]); + break; + + default: + UNIMPLEMENTED(FATAL) << "Unsupported Dex opcode 0x" << std::hex << opcode; + res = true; + } + return res; +} + +void MirConverter::SetDexOffset(int32_t offset) +{ + current_dalvik_offset_ = offset; + ::llvm::SmallVector< ::llvm::Value*, 1> array_ref; + array_ref.push_back(irb_->getInt32(offset)); + ::llvm::MDNode* node = ::llvm::MDNode::get(*context_, array_ref); + irb_->SetDexOffset(node); +} + +// Attach method info as metadata to special intrinsic +void MirConverter::SetMethodInfo() +{ + // We don't want dex offset on this + irb_->SetDexOffset(NULL); + art::llvm::IntrinsicHelper::IntrinsicId id; + id = art::llvm::IntrinsicHelper::MethodInfo; + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Instruction* inst = irb_->CreateCall(intr); + ::llvm::SmallVector< ::llvm::Value*, 2> reg_info; + reg_info.push_back(irb_->getInt32(cu_->num_ins)); + reg_info.push_back(irb_->getInt32(cu_->num_regs)); + reg_info.push_back(irb_->getInt32(cu_->num_outs)); + reg_info.push_back(irb_->getInt32(cu_->num_compiler_temps)); + reg_info.push_back(irb_->getInt32(mir_graph_->GetNumSSARegs())); + ::llvm::MDNode* reg_info_node = ::llvm::MDNode::get(*context_, reg_info); + inst->setMetadata("RegInfo", reg_info_node); + SetDexOffset(current_dalvik_offset_); +} + +void MirConverter::HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb) +{ + SetDexOffset(bb->start_offset); + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + int opcode = mir->dalvikInsn.opcode; + if (opcode < kMirOpFirst) { + // Stop after first non-pseudo MIR op. + continue; + } + if (opcode != kMirOpPhi) { + // Skip other mir Pseudos. + continue; + } + RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]]; + /* + * The Art compiler's Phi nodes only handle 32-bit operands, + * representing wide values using a matched set of Phi nodes + * for the lower and upper halves. In the llvm world, we only + * want a single Phi for wides. Here we will simply discard + * the Phi node representing the high word. + */ + if (rl_dest.high_word) { + continue; // No Phi node - handled via low word + } + int* incoming = reinterpret_cast<int*>(mir->dalvikInsn.vB); + ::llvm::Type* phi_type = + LlvmTypeFromLocRec(rl_dest); + ::llvm::PHINode* phi = irb_->CreatePHI(phi_type, mir->ssa_rep->num_uses); + for (int i = 0; i < mir->ssa_rep->num_uses; i++) { + RegLocation loc; + // Don't check width here. + loc = mir_graph_->GetRawSrc(mir, i); + DCHECK_EQ(rl_dest.wide, loc.wide); + DCHECK_EQ(rl_dest.wide & rl_dest.high_word, loc.wide & loc.high_word); + DCHECK_EQ(rl_dest.fp, loc.fp); + DCHECK_EQ(rl_dest.core, loc.core); + DCHECK_EQ(rl_dest.ref, loc.ref); + SafeMap<unsigned int, unsigned int>::iterator it; + it = mir_graph_->block_id_map_.find(incoming[i]); + DCHECK(it != mir_graph_->block_id_map_.end()); + DCHECK(GetLLVMValue(loc.orig_sreg) != NULL); + DCHECK(GetLLVMBlock(it->second) != NULL); + phi->addIncoming(GetLLVMValue(loc.orig_sreg), + GetLLVMBlock(it->second)); + } + DefineValueOnly(phi, rl_dest.orig_sreg); + } +} + +/* Extended MIR instructions like PHI */ +void MirConverter::ConvertExtendedMIR(BasicBlock* bb, MIR* mir, + ::llvm::BasicBlock* llvm_bb) +{ + + switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) { + case kMirOpPhi: { + // The llvm Phi node already emitted - just DefineValue() here. + RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]]; + if (!rl_dest.high_word) { + // Only consider low word of pairs. + DCHECK(GetLLVMValue(rl_dest.orig_sreg) != NULL); + ::llvm::Value* phi = GetLLVMValue(rl_dest.orig_sreg); + if (1) SetVregOnValue(phi, rl_dest.orig_sreg); + } + break; + } + case kMirOpCopy: { + UNIMPLEMENTED(WARNING) << "unimp kMirOpPhi"; + break; + } + case kMirOpNop: + if ((mir == bb->last_mir_insn) && (bb->taken == NULL) && + (bb->fall_through == NULL)) { + irb_->CreateUnreachable(); + } + break; + + // TODO: need GBC intrinsic to take advantage of fused operations + case kMirOpFusedCmplFloat: + UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpFloat unsupported"; + break; + case kMirOpFusedCmpgFloat: + UNIMPLEMENTED(FATAL) << "kMirOpFusedCmgFloat unsupported"; + break; + case kMirOpFusedCmplDouble: + UNIMPLEMENTED(FATAL) << "kMirOpFusedCmplDouble unsupported"; + break; + case kMirOpFusedCmpgDouble: + UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpgDouble unsupported"; + break; + case kMirOpFusedCmpLong: + UNIMPLEMENTED(FATAL) << "kMirOpLongCmpBranch unsupported"; + break; + default: + break; + } +} + +/* Handle the content in each basic block */ +bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) +{ + if (bb->block_type == kDead) return false; + ::llvm::BasicBlock* llvm_bb = GetLLVMBlock(bb->id); + if (llvm_bb == NULL) { + CHECK(bb->block_type == kExitBlock); + } else { + irb_->SetInsertPoint(llvm_bb); + SetDexOffset(bb->start_offset); + } + + if (cu_->verbose) { + LOG(INFO) << "................................"; + LOG(INFO) << "Block id " << bb->id; + if (llvm_bb != NULL) { + LOG(INFO) << "label " << llvm_bb->getName().str().c_str(); + } else { + LOG(INFO) << "llvm_bb is NULL"; + } + } + + if (bb->block_type == kEntryBlock) { + SetMethodInfo(); + + { // Allocate shadowframe. + art::llvm::IntrinsicHelper::IntrinsicId id = + art::llvm::IntrinsicHelper::AllocaShadowFrame; + ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id); + ::llvm::Value* entries = irb_->getInt32(cu_->num_dalvik_registers); + irb_->CreateCall(func, entries); + } + + { // Store arguments to vregs. + uint16_t arg_reg = cu_->num_regs; + + ::llvm::Function::arg_iterator arg_iter(func_->arg_begin()); + ::llvm::Function::arg_iterator arg_end(func_->arg_end()); + + const char* shorty = cu_->shorty; + uint32_t shorty_size = strlen(shorty); + CHECK_GE(shorty_size, 1u); + + ++arg_iter; // skip method object + + if ((cu_->access_flags & kAccStatic) == 0) { + SetVregOnValue(arg_iter, arg_reg); + ++arg_iter; + ++arg_reg; + } + + for (uint32_t i = 1; i < shorty_size; ++i, ++arg_iter) { + SetVregOnValue(arg_iter, arg_reg); + + ++arg_reg; + if (shorty[i] == 'J' || shorty[i] == 'D') { + // Wide types, such as long and double, are using a pair of registers + // to store the value, so we have to increase arg_reg again. + ++arg_reg; + } + } + } + } else if (bb->block_type == kExitBlock) { + /* + * Because of the differences between how MIR/LIR and llvm handle exit + * blocks, we won't explicitly covert them. On the llvm-to-lir + * path, it will need to be regenereated. + */ + return false; + } else if (bb->block_type == kExceptionHandling) { + /* + * Because we're deferring null checking, delete the associated empty + * exception block. + */ + llvm_bb->eraseFromParent(); + return false; + } + + HandlePhiNodes(bb, llvm_bb); + + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + + SetDexOffset(mir->offset); + + int opcode = mir->dalvikInsn.opcode; + Instruction::Format dalvik_format = + Instruction::FormatOf(mir->dalvikInsn.opcode); + + if (opcode == kMirOpCheck) { + // Combine check and work halves of throwing instruction. + MIR* work_half = mir->meta.throw_insn; + mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode; + opcode = mir->dalvikInsn.opcode; + SSARepresentation* ssa_rep = work_half->ssa_rep; + work_half->ssa_rep = mir->ssa_rep; + mir->ssa_rep = ssa_rep; + work_half->meta.original_opcode = work_half->dalvikInsn.opcode; + work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); + if (bb->successor_block_list.block_list_type == kCatch) { + ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction( + art::llvm::IntrinsicHelper::CatchTargets); + ::llvm::Value* switch_key = + irb_->CreateCall(intr, irb_->getInt32(mir->offset)); + GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_block_list.blocks); + // New basic block to use for work half + ::llvm::BasicBlock* work_bb = + ::llvm::BasicBlock::Create(*context_, "", func_); + ::llvm::SwitchInst* sw = + irb_->CreateSwitch(switch_key, work_bb, + bb->successor_block_list.blocks->Size()); + while (true) { + SuccessorBlockInfo *successor_block_info = iter.Next(); + if (successor_block_info == NULL) break; + ::llvm::BasicBlock *target = + GetLLVMBlock(successor_block_info->block->id); + int type_index = successor_block_info->key; + sw->addCase(irb_->getInt32(type_index), target); + } + llvm_bb = work_bb; + irb_->SetInsertPoint(llvm_bb); + } + } + + if (opcode >= kMirOpFirst) { + ConvertExtendedMIR(bb, mir, llvm_bb); + continue; + } + + bool not_handled = ConvertMIRNode(mir, bb, llvm_bb); + if (not_handled) { + Instruction::Code dalvik_opcode = static_cast<Instruction::Code>(opcode); + LOG(WARNING) << StringPrintf("%#06x: Op %#x (%s) / Fmt %d not handled", + mir->offset, opcode, + Instruction::Name(dalvik_opcode), + dalvik_format); + } + } + + if (bb->block_type == kEntryBlock) { + entry_target_bb_ = GetLLVMBlock(bb->fall_through->id); + } else if ((bb->fall_through != NULL) && !bb->terminated_by_return) { + irb_->CreateBr(GetLLVMBlock(bb->fall_through->id)); + } + + return false; +} + +char RemapShorty(char shorty_type) { + /* + * TODO: might want to revisit this. Dalvik registers are 32-bits wide, + * and longs/doubles are represented as a pair of registers. When sub-word + * arguments (and method results) are passed, they are extended to Dalvik + * virtual register containers. Because llvm is picky about type consistency, + * we must either cast the "real" type to 32-bit container multiple Dalvik + * register types, or always use the expanded values. + * Here, we're doing the latter. We map the shorty signature to container + * types (which is valid so long as we always do a real expansion of passed + * arguments and field loads). + */ + switch(shorty_type) { + case 'Z' : shorty_type = 'I'; break; + case 'B' : shorty_type = 'I'; break; + case 'S' : shorty_type = 'I'; break; + case 'C' : shorty_type = 'I'; break; + default: break; + } + return shorty_type; +} + +::llvm::FunctionType* MirConverter::GetFunctionType() { + + // Get return type + ::llvm::Type* ret_type = irb_->getJType(RemapShorty(cu_->shorty[0])); + + // Get argument type + std::vector< ::llvm::Type*> args_type; + + // method object + args_type.push_back(irb_->getJMethodTy()); + + // Do we have a "this"? + if ((cu_->access_flags & kAccStatic) == 0) { + args_type.push_back(irb_->getJObjectTy()); + } + + for (uint32_t i = 1; i < strlen(cu_->shorty); ++i) { + args_type.push_back(irb_->getJType(RemapShorty(cu_->shorty[i]))); + } + + return ::llvm::FunctionType::get(ret_type, args_type, false); +} + +bool MirConverter::CreateFunction() { + ::llvm::FunctionType* func_type = GetFunctionType(); + if (func_type == NULL) { + return false; + } + + func_ = ::llvm::Function::Create(func_type, + ::llvm::Function::InternalLinkage, + symbol_, module_); + + ::llvm::Function::arg_iterator arg_iter(func_->arg_begin()); + ::llvm::Function::arg_iterator arg_end(func_->arg_end()); + + arg_iter->setName("method"); + ++arg_iter; + + int start_sreg = cu_->num_regs; + + for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) { + arg_iter->setName(StringPrintf("v%i_0", start_sreg)); + start_sreg += mir_graph_->reg_location_[start_sreg].wide ? 2 : 1; + } + + return true; +} + +bool MirConverter::CreateLLVMBasicBlock(BasicBlock* bb) +{ + // Skip the exit block + if ((bb->block_type == kDead) ||(bb->block_type == kExitBlock)) { + id_to_block_map_.Put(bb->id, NULL); + } else { + int offset = bb->start_offset; + bool entry_block = (bb->block_type == kEntryBlock); + ::llvm::BasicBlock* llvm_bb = + ::llvm::BasicBlock::Create(*context_, entry_block ? "entry" : + StringPrintf(kLabelFormat, bb->catch_entry ? kCatchBlock : + kNormalBlock, offset, bb->id), func_); + if (entry_block) { + entry_bb_ = llvm_bb; + placeholder_bb_ = + ::llvm::BasicBlock::Create(*context_, "placeholder", + func_); + } + id_to_block_map_.Put(bb->id, llvm_bb); + } + return false; +} + + +/* + * Convert MIR to LLVM_IR + * o For each ssa name, create LLVM named value. Type these + * appropriately, and ignore high half of wide and double operands. + * o For each MIR basic block, create an LLVM basic block. + * o Iterate through the MIR a basic block at a time, setting arguments + * to recovered ssa name. + */ +void MirConverter::MethodMIR2Bitcode() +{ + InitIR(); + + // Create the function + CreateFunction(); + + // Create an LLVM basic block for each MIR block in dfs preorder + PreOrderDfsIterator iter(mir_graph_, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + CreateLLVMBasicBlock(bb); + } + + /* + * Create an llvm named value for each MIR SSA name. Note: we'll use + * placeholders for all non-argument values (because we haven't seen + * the definition yet). + */ + irb_->SetInsertPoint(placeholder_bb_); + ::llvm::Function::arg_iterator arg_iter(func_->arg_begin()); + arg_iter++; /* Skip path method */ + for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) { + ::llvm::Value* val; + RegLocation rl_temp = mir_graph_->reg_location_[i]; + if ((mir_graph_->SRegToVReg(i) < 0) || rl_temp.high_word) { + llvm_values_.Insert(0); + } else if ((i < cu_->num_regs) || + (i >= (cu_->num_regs + cu_->num_ins))) { + ::llvm::Constant* imm_value = mir_graph_->reg_location_[i].wide ? + irb_->getJLong(0) : irb_->getJInt(0); + val = EmitConst(imm_value, mir_graph_->reg_location_[i]); + val->setName(mir_graph_->GetSSAName(i)); + llvm_values_.Insert(val); + } else { + // Recover previously-created argument values + ::llvm::Value* arg_val = arg_iter++; + llvm_values_.Insert(arg_val); + } + } + + PreOrderDfsIterator iter2(mir_graph_, false /* not iterative */); + for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { + BlockBitcodeConversion(bb); + } + + /* + * In a few rare cases of verification failure, the verifier will + * replace one or more Dalvik opcodes with the special + * throw-verification-failure opcode. This can leave the SSA graph + * in an invalid state, as definitions may be lost, while uses retained. + * To work around this problem, we insert placeholder definitions for + * all Dalvik SSA regs in the "placeholder" block. Here, after + * bitcode conversion is complete, we examine those placeholder definitions + * and delete any with no references (which normally is all of them). + * + * If any definitions remain, we link the placeholder block into the + * CFG. Otherwise, it is deleted. + */ + for (::llvm::BasicBlock::iterator it = placeholder_bb_->begin(), + it_end = placeholder_bb_->end(); it != it_end;) { + ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(it++); + DCHECK(inst != NULL); + ::llvm::Value* val = ::llvm::dyn_cast< ::llvm::Value>(inst); + DCHECK(val != NULL); + if (val->getNumUses() == 0) { + inst->eraseFromParent(); + } + } + SetDexOffset(0); + if (placeholder_bb_->empty()) { + placeholder_bb_->eraseFromParent(); + } else { + irb_->SetInsertPoint(placeholder_bb_); + irb_->CreateBr(entry_target_bb_); + entry_target_bb_ = placeholder_bb_; + } + irb_->SetInsertPoint(entry_bb_); + irb_->CreateBr(entry_target_bb_); + + if (cu_->enable_debug & (1 << kDebugVerifyBitcode)) { + if (::llvm::verifyFunction(*func_, ::llvm::PrintMessageAction)) { + LOG(INFO) << "Bitcode verification FAILED for " + << PrettyMethod(cu_->method_idx, *cu_->dex_file) + << " of size " << cu_->code_item->insns_size_in_code_units_; + cu_->enable_debug |= (1 << kDebugDumpBitcodeFile); + } + } + + if (cu_->enable_debug & (1 << kDebugDumpBitcodeFile)) { + // Write bitcode to file + std::string errmsg; + std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file)); + mir_graph_->ReplaceSpecialChars(fname); + // TODO: make configurable change naming mechanism to avoid fname length issues. + fname = StringPrintf("/sdcard/Bitcode/%s.bc", fname.c_str()); + + if (fname.size() > 240) { + LOG(INFO) << "Warning: bitcode filename too long. Truncated."; + fname.resize(240); + } + + ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( + new ::llvm::tool_output_file(fname.c_str(), errmsg, + ::llvm::raw_fd_ostream::F_Binary)); + + if (!errmsg.empty()) { + LOG(ERROR) << "Failed to create bitcode output file: " << errmsg; + } + + ::llvm::WriteBitcodeToFile(module_, out_file->os()); + out_file->keep(); + } +} + +Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, + ArenaAllocator* const arena, + llvm::LlvmCompilationUnit* const llvm_compilation_unit) { + return new MirConverter(cu, mir_graph, arena, llvm_compilation_unit); +} + +} // namespace art |