| /* |
| * Copyright (C) 2014 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 "quick_compiler.h" |
| |
| #include <cstdint> |
| |
| #include "art_method-inl.h" |
| #include "base/dumpable.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/timing_logger.h" |
| #include "compiler.h" |
| #include "dex_file-inl.h" |
| #include "dex_file_to_method_inliner_map.h" |
| #include "dex/compiler_ir.h" |
| #include "dex/dex_flags.h" |
| #include "dex/mir_graph.h" |
| #include "dex/pass_driver_me_opts.h" |
| #include "dex/pass_driver_me_post_opt.h" |
| #include "dex/pass_manager.h" |
| #include "dex/quick/mir_to_lir.h" |
| #include "dex/verified_method.h" |
| #include "driver/compiler_driver.h" |
| #include "driver/compiler_options.h" |
| #include "elf_writer_quick.h" |
| #include "jni/quick/jni_compiler.h" |
| #include "mir_to_lir.h" |
| #include "mirror/object.h" |
| #include "runtime.h" |
| |
| // Specific compiler backends. |
| #ifdef ART_ENABLE_CODEGEN_arm |
| #include "dex/quick/arm/backend_arm.h" |
| #endif |
| |
| #ifdef ART_ENABLE_CODEGEN_arm64 |
| #include "dex/quick/arm64/backend_arm64.h" |
| #endif |
| |
| #if defined(ART_ENABLE_CODEGEN_mips) || defined(ART_ENABLE_CODEGEN_mips64) |
| #include "dex/quick/mips/backend_mips.h" |
| #endif |
| |
| #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) |
| #include "dex/quick/x86/backend_x86.h" |
| #endif |
| |
| namespace art { |
| |
| static_assert(0U == static_cast<size_t>(kNone), "kNone not 0"); |
| static_assert(1U == static_cast<size_t>(kArm), "kArm not 1"); |
| static_assert(2U == static_cast<size_t>(kArm64), "kArm64 not 2"); |
| static_assert(3U == static_cast<size_t>(kThumb2), "kThumb2 not 3"); |
| static_assert(4U == static_cast<size_t>(kX86), "kX86 not 4"); |
| static_assert(5U == static_cast<size_t>(kX86_64), "kX86_64 not 5"); |
| static_assert(6U == static_cast<size_t>(kMips), "kMips not 6"); |
| static_assert(7U == static_cast<size_t>(kMips64), "kMips64 not 7"); |
| |
| // Additional disabled optimizations (over generally disabled) per instruction set. |
| static constexpr uint32_t kDisabledOptimizationsPerISA[] = { |
| // 0 = kNone. |
| ~0U, |
| // 1 = kArm, unused (will use kThumb2). |
| ~0U, |
| // 2 = kArm64. |
| 0, |
| // 3 = kThumb2. |
| 0, |
| // 4 = kX86. |
| (1 << kLoadStoreElimination) | |
| 0, |
| // 5 = kX86_64. |
| (1 << kLoadStoreElimination) | |
| 0, |
| // 6 = kMips. |
| (1 << kLoadStoreElimination) | |
| (1 << kLoadHoisting) | |
| (1 << kSuppressLoads) | |
| (1 << kNullCheckElimination) | |
| (1 << kPromoteRegs) | |
| (1 << kTrackLiveTemps) | |
| (1 << kSafeOptimizations) | |
| (1 << kBBOpt) | |
| (1 << kMatch) | |
| (1 << kPromoteCompilerTemps) | |
| 0, |
| // 7 = kMips64. |
| (1 << kLoadStoreElimination) | |
| (1 << kLoadHoisting) | |
| (1 << kSuppressLoads) | |
| (1 << kNullCheckElimination) | |
| (1 << kPromoteRegs) | |
| (1 << kTrackLiveTemps) | |
| (1 << kSafeOptimizations) | |
| (1 << kBBOpt) | |
| (1 << kMatch) | |
| (1 << kPromoteCompilerTemps) | |
| 0 |
| }; |
| static_assert(sizeof(kDisabledOptimizationsPerISA) == 8 * sizeof(uint32_t), |
| "kDisabledOpts unexpected"); |
| |
| // Supported shorty types per instruction set. null means that all are available. |
| // Z : boolean |
| // B : byte |
| // S : short |
| // C : char |
| // I : int |
| // J : long |
| // F : float |
| // D : double |
| // L : reference(object, array) |
| // V : void |
| static const char* kSupportedTypes[] = { |
| // 0 = kNone. |
| "", |
| // 1 = kArm, unused (will use kThumb2). |
| "", |
| // 2 = kArm64. |
| nullptr, |
| // 3 = kThumb2. |
| nullptr, |
| // 4 = kX86. |
| nullptr, |
| // 5 = kX86_64. |
| nullptr, |
| // 6 = kMips. |
| nullptr, |
| // 7 = kMips64. |
| nullptr |
| }; |
| static_assert(sizeof(kSupportedTypes) == 8 * sizeof(char*), "kSupportedTypes unexpected"); |
| |
| static int kAllOpcodes[] = { |
| Instruction::NOP, |
| Instruction::MOVE, |
| Instruction::MOVE_FROM16, |
| Instruction::MOVE_16, |
| Instruction::MOVE_WIDE, |
| Instruction::MOVE_WIDE_FROM16, |
| Instruction::MOVE_WIDE_16, |
| Instruction::MOVE_OBJECT, |
| Instruction::MOVE_OBJECT_FROM16, |
| Instruction::MOVE_OBJECT_16, |
| Instruction::MOVE_RESULT, |
| Instruction::MOVE_RESULT_WIDE, |
| Instruction::MOVE_RESULT_OBJECT, |
| Instruction::MOVE_EXCEPTION, |
| Instruction::RETURN_VOID, |
| Instruction::RETURN, |
| Instruction::RETURN_WIDE, |
| Instruction::RETURN_OBJECT, |
| Instruction::CONST_4, |
| Instruction::CONST_16, |
| Instruction::CONST, |
| Instruction::CONST_HIGH16, |
| Instruction::CONST_WIDE_16, |
| Instruction::CONST_WIDE_32, |
| Instruction::CONST_WIDE, |
| Instruction::CONST_WIDE_HIGH16, |
| Instruction::CONST_STRING, |
| Instruction::CONST_STRING_JUMBO, |
| Instruction::CONST_CLASS, |
| Instruction::MONITOR_ENTER, |
| Instruction::MONITOR_EXIT, |
| Instruction::CHECK_CAST, |
| Instruction::INSTANCE_OF, |
| Instruction::ARRAY_LENGTH, |
| Instruction::NEW_INSTANCE, |
| Instruction::NEW_ARRAY, |
| Instruction::FILLED_NEW_ARRAY, |
| Instruction::FILLED_NEW_ARRAY_RANGE, |
| Instruction::FILL_ARRAY_DATA, |
| Instruction::THROW, |
| Instruction::GOTO, |
| Instruction::GOTO_16, |
| Instruction::GOTO_32, |
| Instruction::PACKED_SWITCH, |
| Instruction::SPARSE_SWITCH, |
| Instruction::CMPL_FLOAT, |
| Instruction::CMPG_FLOAT, |
| Instruction::CMPL_DOUBLE, |
| Instruction::CMPG_DOUBLE, |
| Instruction::CMP_LONG, |
| Instruction::IF_EQ, |
| Instruction::IF_NE, |
| Instruction::IF_LT, |
| Instruction::IF_GE, |
| Instruction::IF_GT, |
| Instruction::IF_LE, |
| Instruction::IF_EQZ, |
| Instruction::IF_NEZ, |
| Instruction::IF_LTZ, |
| Instruction::IF_GEZ, |
| Instruction::IF_GTZ, |
| Instruction::IF_LEZ, |
| Instruction::UNUSED_3E, |
| Instruction::UNUSED_3F, |
| Instruction::UNUSED_40, |
| Instruction::UNUSED_41, |
| Instruction::UNUSED_42, |
| Instruction::UNUSED_43, |
| Instruction::AGET, |
| Instruction::AGET_WIDE, |
| Instruction::AGET_OBJECT, |
| Instruction::AGET_BOOLEAN, |
| Instruction::AGET_BYTE, |
| Instruction::AGET_CHAR, |
| Instruction::AGET_SHORT, |
| Instruction::APUT, |
| Instruction::APUT_WIDE, |
| Instruction::APUT_OBJECT, |
| Instruction::APUT_BOOLEAN, |
| Instruction::APUT_BYTE, |
| Instruction::APUT_CHAR, |
| Instruction::APUT_SHORT, |
| Instruction::IGET, |
| Instruction::IGET_WIDE, |
| Instruction::IGET_OBJECT, |
| Instruction::IGET_BOOLEAN, |
| Instruction::IGET_BYTE, |
| Instruction::IGET_CHAR, |
| Instruction::IGET_SHORT, |
| Instruction::IPUT, |
| Instruction::IPUT_WIDE, |
| Instruction::IPUT_OBJECT, |
| Instruction::IPUT_BOOLEAN, |
| Instruction::IPUT_BYTE, |
| Instruction::IPUT_CHAR, |
| Instruction::IPUT_SHORT, |
| Instruction::SGET, |
| Instruction::SGET_WIDE, |
| Instruction::SGET_OBJECT, |
| Instruction::SGET_BOOLEAN, |
| Instruction::SGET_BYTE, |
| Instruction::SGET_CHAR, |
| Instruction::SGET_SHORT, |
| Instruction::SPUT, |
| Instruction::SPUT_WIDE, |
| Instruction::SPUT_OBJECT, |
| Instruction::SPUT_BOOLEAN, |
| Instruction::SPUT_BYTE, |
| Instruction::SPUT_CHAR, |
| Instruction::SPUT_SHORT, |
| Instruction::INVOKE_VIRTUAL, |
| Instruction::INVOKE_SUPER, |
| Instruction::INVOKE_DIRECT, |
| Instruction::INVOKE_STATIC, |
| Instruction::INVOKE_INTERFACE, |
| Instruction::RETURN_VOID_NO_BARRIER, |
| Instruction::INVOKE_VIRTUAL_RANGE, |
| Instruction::INVOKE_SUPER_RANGE, |
| Instruction::INVOKE_DIRECT_RANGE, |
| Instruction::INVOKE_STATIC_RANGE, |
| Instruction::INVOKE_INTERFACE_RANGE, |
| Instruction::UNUSED_79, |
| Instruction::UNUSED_7A, |
| Instruction::NEG_INT, |
| Instruction::NOT_INT, |
| Instruction::NEG_LONG, |
| Instruction::NOT_LONG, |
| Instruction::NEG_FLOAT, |
| Instruction::NEG_DOUBLE, |
| Instruction::INT_TO_LONG, |
| Instruction::INT_TO_FLOAT, |
| Instruction::INT_TO_DOUBLE, |
| Instruction::LONG_TO_INT, |
| Instruction::LONG_TO_FLOAT, |
| Instruction::LONG_TO_DOUBLE, |
| Instruction::FLOAT_TO_INT, |
| Instruction::FLOAT_TO_LONG, |
| Instruction::FLOAT_TO_DOUBLE, |
| Instruction::DOUBLE_TO_INT, |
| Instruction::DOUBLE_TO_LONG, |
| Instruction::DOUBLE_TO_FLOAT, |
| Instruction::INT_TO_BYTE, |
| Instruction::INT_TO_CHAR, |
| Instruction::INT_TO_SHORT, |
| Instruction::ADD_INT, |
| Instruction::SUB_INT, |
| Instruction::MUL_INT, |
| Instruction::DIV_INT, |
| Instruction::REM_INT, |
| Instruction::AND_INT, |
| Instruction::OR_INT, |
| Instruction::XOR_INT, |
| Instruction::SHL_INT, |
| Instruction::SHR_INT, |
| Instruction::USHR_INT, |
| Instruction::ADD_LONG, |
| Instruction::SUB_LONG, |
| Instruction::MUL_LONG, |
| Instruction::DIV_LONG, |
| Instruction::REM_LONG, |
| Instruction::AND_LONG, |
| Instruction::OR_LONG, |
| Instruction::XOR_LONG, |
| Instruction::SHL_LONG, |
| Instruction::SHR_LONG, |
| Instruction::USHR_LONG, |
| Instruction::ADD_FLOAT, |
| Instruction::SUB_FLOAT, |
| Instruction::MUL_FLOAT, |
| Instruction::DIV_FLOAT, |
| Instruction::REM_FLOAT, |
| Instruction::ADD_DOUBLE, |
| Instruction::SUB_DOUBLE, |
| Instruction::MUL_DOUBLE, |
| Instruction::DIV_DOUBLE, |
| Instruction::REM_DOUBLE, |
| Instruction::ADD_INT_2ADDR, |
| Instruction::SUB_INT_2ADDR, |
| Instruction::MUL_INT_2ADDR, |
| Instruction::DIV_INT_2ADDR, |
| Instruction::REM_INT_2ADDR, |
| Instruction::AND_INT_2ADDR, |
| Instruction::OR_INT_2ADDR, |
| Instruction::XOR_INT_2ADDR, |
| Instruction::SHL_INT_2ADDR, |
| Instruction::SHR_INT_2ADDR, |
| Instruction::USHR_INT_2ADDR, |
| Instruction::ADD_LONG_2ADDR, |
| Instruction::SUB_LONG_2ADDR, |
| Instruction::MUL_LONG_2ADDR, |
| Instruction::DIV_LONG_2ADDR, |
| Instruction::REM_LONG_2ADDR, |
| Instruction::AND_LONG_2ADDR, |
| Instruction::OR_LONG_2ADDR, |
| Instruction::XOR_LONG_2ADDR, |
| Instruction::SHL_LONG_2ADDR, |
| Instruction::SHR_LONG_2ADDR, |
| Instruction::USHR_LONG_2ADDR, |
| Instruction::ADD_FLOAT_2ADDR, |
| Instruction::SUB_FLOAT_2ADDR, |
| Instruction::MUL_FLOAT_2ADDR, |
| Instruction::DIV_FLOAT_2ADDR, |
| Instruction::REM_FLOAT_2ADDR, |
| Instruction::ADD_DOUBLE_2ADDR, |
| Instruction::SUB_DOUBLE_2ADDR, |
| Instruction::MUL_DOUBLE_2ADDR, |
| Instruction::DIV_DOUBLE_2ADDR, |
| Instruction::REM_DOUBLE_2ADDR, |
| Instruction::ADD_INT_LIT16, |
| Instruction::RSUB_INT, |
| Instruction::MUL_INT_LIT16, |
| Instruction::DIV_INT_LIT16, |
| Instruction::REM_INT_LIT16, |
| Instruction::AND_INT_LIT16, |
| Instruction::OR_INT_LIT16, |
| Instruction::XOR_INT_LIT16, |
| Instruction::ADD_INT_LIT8, |
| Instruction::RSUB_INT_LIT8, |
| Instruction::MUL_INT_LIT8, |
| Instruction::DIV_INT_LIT8, |
| Instruction::REM_INT_LIT8, |
| Instruction::AND_INT_LIT8, |
| Instruction::OR_INT_LIT8, |
| Instruction::XOR_INT_LIT8, |
| Instruction::SHL_INT_LIT8, |
| Instruction::SHR_INT_LIT8, |
| Instruction::USHR_INT_LIT8, |
| Instruction::IGET_QUICK, |
| Instruction::IGET_WIDE_QUICK, |
| Instruction::IGET_OBJECT_QUICK, |
| Instruction::IPUT_QUICK, |
| Instruction::IPUT_WIDE_QUICK, |
| Instruction::IPUT_OBJECT_QUICK, |
| Instruction::INVOKE_VIRTUAL_QUICK, |
| Instruction::INVOKE_VIRTUAL_RANGE_QUICK, |
| Instruction::IPUT_BOOLEAN_QUICK, |
| Instruction::IPUT_BYTE_QUICK, |
| Instruction::IPUT_CHAR_QUICK, |
| Instruction::IPUT_SHORT_QUICK, |
| Instruction::IGET_BOOLEAN_QUICK, |
| Instruction::IGET_BYTE_QUICK, |
| Instruction::IGET_CHAR_QUICK, |
| Instruction::IGET_SHORT_QUICK, |
| Instruction::INVOKE_LAMBDA, |
| Instruction::UNUSED_F4, |
| Instruction::CAPTURE_VARIABLE, |
| Instruction::CREATE_LAMBDA, |
| Instruction::LIBERATE_VARIABLE, |
| Instruction::BOX_LAMBDA, |
| Instruction::UNBOX_LAMBDA, |
| Instruction::UNUSED_FA, |
| Instruction::UNUSED_FB, |
| Instruction::UNUSED_FC, |
| Instruction::UNUSED_FD, |
| Instruction::UNUSED_FE, |
| Instruction::UNUSED_FF, |
| // ----- ExtendedMIROpcode ----- |
| kMirOpPhi, |
| kMirOpCopy, |
| kMirOpFusedCmplFloat, |
| kMirOpFusedCmpgFloat, |
| kMirOpFusedCmplDouble, |
| kMirOpFusedCmpgDouble, |
| kMirOpFusedCmpLong, |
| kMirOpNop, |
| kMirOpNullCheck, |
| kMirOpRangeCheck, |
| kMirOpDivZeroCheck, |
| kMirOpCheck, |
| kMirOpSelect, |
| }; |
| |
| static int kInvokeOpcodes[] = { |
| Instruction::INVOKE_VIRTUAL, |
| Instruction::INVOKE_SUPER, |
| Instruction::INVOKE_DIRECT, |
| Instruction::INVOKE_STATIC, |
| Instruction::INVOKE_INTERFACE, |
| Instruction::INVOKE_VIRTUAL_RANGE, |
| Instruction::INVOKE_SUPER_RANGE, |
| Instruction::INVOKE_DIRECT_RANGE, |
| Instruction::INVOKE_STATIC_RANGE, |
| Instruction::INVOKE_INTERFACE_RANGE, |
| Instruction::INVOKE_VIRTUAL_QUICK, |
| Instruction::INVOKE_VIRTUAL_RANGE_QUICK, |
| }; |
| |
| // TODO: Add support for lambda opcodes to the quick compiler. |
| static const int kUnsupportedLambdaOpcodes[] = { |
| Instruction::INVOKE_LAMBDA, |
| Instruction::CREATE_LAMBDA, |
| Instruction::BOX_LAMBDA, |
| Instruction::UNBOX_LAMBDA, |
| }; |
| |
| // Unsupported opcodes. Null can be used when everything is supported. Size of the lists is |
| // recorded below. |
| static const int* kUnsupportedOpcodes[] = { |
| // 0 = kNone. |
| kAllOpcodes, |
| // 1 = kArm, unused (will use kThumb2). |
| kAllOpcodes, |
| // 2 = kArm64. |
| kUnsupportedLambdaOpcodes, |
| // 3 = kThumb2. |
| kUnsupportedLambdaOpcodes, |
| // 4 = kX86. |
| kUnsupportedLambdaOpcodes, |
| // 5 = kX86_64. |
| kUnsupportedLambdaOpcodes, |
| // 6 = kMips. |
| kUnsupportedLambdaOpcodes, |
| // 7 = kMips64. |
| kUnsupportedLambdaOpcodes, |
| }; |
| static_assert(sizeof(kUnsupportedOpcodes) == 8 * sizeof(int*), "kUnsupportedOpcodes unexpected"); |
| |
| // Size of the arrays stored above. |
| static const size_t kUnsupportedOpcodesSize[] = { |
| // 0 = kNone. |
| arraysize(kAllOpcodes), |
| // 1 = kArm, unused (will use kThumb2). |
| arraysize(kAllOpcodes), |
| // 2 = kArm64. |
| arraysize(kUnsupportedLambdaOpcodes), |
| // 3 = kThumb2. |
| arraysize(kUnsupportedLambdaOpcodes), |
| // 4 = kX86. |
| arraysize(kUnsupportedLambdaOpcodes), |
| // 5 = kX86_64. |
| arraysize(kUnsupportedLambdaOpcodes), |
| // 6 = kMips. |
| arraysize(kUnsupportedLambdaOpcodes), |
| // 7 = kMips64. |
| arraysize(kUnsupportedLambdaOpcodes), |
| }; |
| static_assert(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t), |
| "kUnsupportedOpcodesSize unexpected"); |
| |
| static bool IsUnsupportedExperimentalLambdasOnly(size_t i) { |
| DCHECK_LE(i, arraysize(kUnsupportedOpcodes)); |
| return kUnsupportedOpcodes[i] == kUnsupportedLambdaOpcodes; |
| } |
| |
| // The maximum amount of Dalvik register in a method for which we will start compiling. Tries to |
| // avoid an abort when we need to manage more SSA registers than we can. |
| static constexpr size_t kMaxAllowedDalvikRegisters = INT16_MAX / 2; |
| |
| static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) { |
| const char* supported_types = kSupportedTypes[instruction_set]; |
| if (supported_types == nullptr) { |
| // Everything available. |
| return true; |
| } |
| |
| uint32_t shorty_size = strlen(shorty); |
| CHECK_GE(shorty_size, 1u); |
| |
| for (uint32_t i = 0; i < shorty_size; i++) { |
| if (strchr(supported_types, shorty[i]) == nullptr) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // If the ISA has unsupported opcodes, should we skip scanning over them? |
| // |
| // Most of the time we're compiling non-experimental files, so scanning just slows |
| // performance down by as much as 6% with 4 threads. |
| // In the rare cases we compile experimental opcodes, the runtime has an option to enable it, |
| // which will force scanning for any unsupported opcodes. |
| static bool SkipScanningUnsupportedOpcodes(InstructionSet instruction_set) { |
| if (UNLIKELY(kUnsupportedOpcodesSize[instruction_set] == 0U)) { |
| // All opcodes are supported no matter what. Usually not the case |
| // since experimental opcodes are not implemented in the quick compiler. |
| return true; |
| } else if (LIKELY(!Runtime::Current()->AreExperimentalLambdasEnabled())) { |
| // Experimental opcodes are disabled. |
| // |
| // If all unsupported opcodes are experimental we don't need to do scanning. |
| return IsUnsupportedExperimentalLambdasOnly(instruction_set); |
| } else { |
| // Experimental opcodes are enabled. |
| // |
| // Do the opcode scanning if the ISA has any unsupported opcodes. |
| return false; |
| } |
| } |
| |
| // Skip the method that we do not support currently. |
| bool QuickCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, |
| CompilationUnit* cu) const { |
| // This is a limitation in mir_graph. See MirGraph::SetNumSSARegs. |
| if (cu->mir_graph->GetNumOfCodeAndTempVRs() > kMaxAllowedDalvikRegisters) { |
| VLOG(compiler) << "Too many dalvik registers : " << cu->mir_graph->GetNumOfCodeAndTempVRs(); |
| return false; |
| } |
| |
| // Check whether we do have limitations at all. |
| if (kSupportedTypes[cu->instruction_set] == nullptr && |
| SkipScanningUnsupportedOpcodes(cu->instruction_set)) { |
| return true; |
| } |
| |
| // Check if we can compile the prototype. |
| const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx)); |
| if (!CanCompileShorty(shorty, cu->instruction_set)) { |
| VLOG(compiler) << "Unsupported shorty : " << shorty; |
| return false; |
| } |
| |
| const int *unsupport_list = kUnsupportedOpcodes[cu->instruction_set]; |
| int unsupport_list_size = kUnsupportedOpcodesSize[cu->instruction_set]; |
| |
| for (unsigned int idx = 0; idx < cu->mir_graph->GetNumBlocks(); idx++) { |
| BasicBlock* bb = cu->mir_graph->GetBasicBlock(idx); |
| if (bb == nullptr) continue; |
| if (bb->block_type == kDead) continue; |
| for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { |
| int opcode = mir->dalvikInsn.opcode; |
| // Check if we support the byte code. |
| if (std::find(unsupport_list, unsupport_list + unsupport_list_size, opcode) |
| != unsupport_list + unsupport_list_size) { |
| if (!MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { |
| VLOG(compiler) << "Unsupported dalvik byte code : " |
| << mir->dalvikInsn.opcode; |
| } else { |
| VLOG(compiler) << "Unsupported extended MIR opcode : " |
| << MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst]; |
| } |
| return false; |
| } |
| // Check if it invokes a prototype that we cannot support. |
| if (std::find(kInvokeOpcodes, kInvokeOpcodes + arraysize(kInvokeOpcodes), opcode) |
| != kInvokeOpcodes + arraysize(kInvokeOpcodes)) { |
| uint32_t invoke_method_idx = mir->dalvikInsn.vB; |
| const char* invoke_method_shorty = dex_file.GetMethodShorty( |
| dex_file.GetMethodId(invoke_method_idx)); |
| if (!CanCompileShorty(invoke_method_shorty, cu->instruction_set)) { |
| VLOG(compiler) << "Unsupported to invoke '" |
| << PrettyMethod(invoke_method_idx, dex_file) |
| << "' with shorty : " << invoke_method_shorty; |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| void QuickCompiler::InitCompilationUnit(CompilationUnit& cu) const { |
| // Disable optimizations according to instruction set. |
| cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set]; |
| if (Runtime::Current()->UseJit()) { |
| // Disable these optimizations for JIT until quickened byte codes are done being implemented. |
| // TODO: Find a cleaner way to do this. |
| cu.disable_opt |= 1u << kLocalValueNumbering; |
| } |
| } |
| |
| void QuickCompiler::Init() { |
| CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr); |
| } |
| |
| void QuickCompiler::UnInit() const { |
| CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr); |
| } |
| |
| /* Default optimizer/debug setting for the compiler. */ |
| static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations |
| // (1 << kLoadStoreElimination) | |
| // (1 << kLoadHoisting) | |
| // (1 << kSuppressLoads) | |
| // (1 << kNullCheckElimination) | |
| // (1 << kClassInitCheckElimination) | |
| // (1 << kGlobalValueNumbering) | |
| // (1 << kGvnDeadCodeElimination) | |
| // (1 << kLocalValueNumbering) | |
| // (1 << kPromoteRegs) | |
| // (1 << kTrackLiveTemps) | |
| // (1 << kSafeOptimizations) | |
| // (1 << kBBOpt) | |
| // (1 << kSuspendCheckElimination) | |
| // (1 << kMatch) | |
| // (1 << kPromoteCompilerTemps) | |
| // (1 << kSuppressExceptionEdges) | |
| // (1 << kSuppressMethodInlining) | |
| 0; |
| |
| static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes |
| // (1 << kDebugDisplayMissingTargets) | |
| // (1 << kDebugVerbose) | |
| // (1 << kDebugDumpCFG) | |
| // (1 << kDebugSlowFieldPath) | |
| // (1 << kDebugSlowInvokePath) | |
| // (1 << kDebugSlowStringPath) | |
| // (1 << kDebugSlowestFieldPath) | |
| // (1 << kDebugSlowestStringPath) | |
| // (1 << kDebugExerciseResolveMethod) | |
| // (1 << kDebugVerifyDataflow) | |
| // (1 << kDebugShowMemoryUsage) | |
| // (1 << kDebugShowNops) | |
| // (1 << kDebugCountOpcodes) | |
| // (1 << kDebugDumpCheckStats) | |
| // (1 << kDebugShowSummaryMemoryUsage) | |
| // (1 << kDebugShowFilterStats) | |
| // (1 << kDebugTimings) | |
| // (1 << kDebugCodegenDump) | |
| 0; |
| |
| CompiledMethod* QuickCompiler::Compile(const DexFile::CodeItem* code_item, |
| uint32_t access_flags, |
| InvokeType invoke_type, |
| uint16_t class_def_idx, |
| uint32_t method_idx, |
| jobject class_loader, |
| const DexFile& dex_file, |
| Handle<mirror::DexCache> dex_cache) const { |
| if (kPoisonHeapReferences) { |
| VLOG(compiler) << "Skipping method : " << PrettyMethod(method_idx, dex_file) |
| << " Reason = Quick does not support heap poisoning."; |
| return nullptr; |
| } |
| |
| // TODO: check method fingerprint here to determine appropriate backend type. Until then, use |
| // build default. |
| CompilerDriver* driver = GetCompilerDriver(); |
| |
| VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "..."; |
| if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) { |
| return nullptr; |
| } |
| |
| DCHECK(driver->GetCompilerOptions().IsCompilationEnabled()); |
| DCHECK(!driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow()); |
| |
| Runtime* const runtime = Runtime::Current(); |
| ClassLinker* const class_linker = runtime->GetClassLinker(); |
| InstructionSet instruction_set = driver->GetInstructionSet(); |
| if (instruction_set == kArm) { |
| instruction_set = kThumb2; |
| } |
| CompilationUnit cu(runtime->GetArenaPool(), instruction_set, driver, class_linker); |
| cu.dex_file = &dex_file; |
| cu.class_def_idx = class_def_idx; |
| cu.method_idx = method_idx; |
| cu.access_flags = access_flags; |
| cu.invoke_type = invoke_type; |
| cu.shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx)); |
| |
| CHECK((cu.instruction_set == kThumb2) || |
| (cu.instruction_set == kArm64) || |
| (cu.instruction_set == kX86) || |
| (cu.instruction_set == kX86_64) || |
| (cu.instruction_set == kMips) || |
| (cu.instruction_set == kMips64)); |
| |
| // TODO: set this from command line |
| constexpr bool compiler_flip_match = false; |
| const std::string compiler_method_match = ""; |
| |
| bool use_match = !compiler_method_match.empty(); |
| bool match = use_match && (compiler_flip_match ^ |
| (PrettyMethod(method_idx, dex_file).find(compiler_method_match) != std::string::npos)); |
| if (!use_match || match) { |
| cu.disable_opt = kCompilerOptimizerDisableFlags; |
| cu.enable_debug = kCompilerDebugFlags; |
| cu.verbose = VLOG_IS_ON(compiler) || |
| (cu.enable_debug & (1 << kDebugVerbose)); |
| } |
| |
| if (driver->GetCompilerOptions().HasVerboseMethods()) { |
| cu.verbose = driver->GetCompilerOptions().IsVerboseMethod(PrettyMethod(method_idx, dex_file)); |
| } |
| |
| if (cu.verbose) { |
| cu.enable_debug |= (1 << kDebugCodegenDump); |
| } |
| |
| /* |
| * TODO: rework handling of optimization and debug flags. Should we split out |
| * MIR and backend flags? Need command-line setting as well. |
| */ |
| |
| InitCompilationUnit(cu); |
| |
| cu.StartTimingSplit("BuildMIRGraph"); |
| cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena)); |
| |
| /* |
| * After creation of the MIR graph, also create the code generator. |
| * The reason we do this is that optimizations on the MIR graph may need to get information |
| * that is only available if a CG exists. |
| */ |
| cu.cg.reset(GetCodeGenerator(&cu, nullptr)); |
| |
| /* Gathering opcode stats? */ |
| if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) { |
| cu.mir_graph->EnableOpcodeCounting(); |
| } |
| |
| /* Build the raw MIR graph */ |
| cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, |
| class_loader, dex_file, dex_cache); |
| |
| if (!CanCompileMethod(method_idx, dex_file, &cu)) { |
| VLOG(compiler) << cu.instruction_set << ": Cannot compile method : " |
| << PrettyMethod(method_idx, dex_file); |
| cu.EndTiming(); |
| return nullptr; |
| } |
| |
| cu.NewTimingSplit("MIROpt:CheckFilters"); |
| std::string skip_message; |
| if (cu.mir_graph->SkipCompilation(&skip_message)) { |
| VLOG(compiler) << cu.instruction_set << ": Skipping method : " |
| << PrettyMethod(method_idx, dex_file) << " Reason = " << skip_message; |
| cu.EndTiming(); |
| return nullptr; |
| } |
| |
| /* Create the pass driver and launch it */ |
| PassDriverMEOpts pass_driver(GetPreOptPassManager(), GetPostOptPassManager(), &cu); |
| pass_driver.Launch(); |
| |
| /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */ |
| if (cu.compiler_driver->ProfilePresent() |
| && !cu.mir_graph->MethodIsLeaf() |
| && cu.mir_graph->SkipCompilationByName(PrettyMethod(method_idx, dex_file))) { |
| cu.EndTiming(); |
| return nullptr; |
| } |
| |
| if (cu.enable_debug & (1 << kDebugDumpCheckStats)) { |
| cu.mir_graph->DumpCheckStats(); |
| } |
| |
| if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) { |
| cu.mir_graph->ShowOpcodeStats(); |
| } |
| |
| /* Reassociate sreg names with original Dalvik vreg names. */ |
| cu.mir_graph->RemapRegLocations(); |
| |
| /* Free Arenas from the cu.arena_stack for reuse by the cu.arena in the codegen. */ |
| if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) { |
| if (cu.arena_stack.PeakBytesAllocated() > 1 * 1024 * 1024) { |
| MemStats stack_stats(cu.arena_stack.GetPeakStats()); |
| LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(stack_stats); |
| } |
| } |
| cu.arena_stack.Reset(); |
| |
| CompiledMethod* result = nullptr; |
| |
| if (cu.mir_graph->PuntToInterpreter()) { |
| VLOG(compiler) << cu.instruction_set << ": Punted method to interpreter: " |
| << PrettyMethod(method_idx, dex_file); |
| cu.EndTiming(); |
| return nullptr; |
| } |
| |
| cu.cg->Materialize(); |
| |
| cu.NewTimingSplit("Dedupe"); /* deduping takes up the vast majority of time in GetCompiledMethod(). */ |
| result = cu.cg->GetCompiledMethod(); |
| cu.NewTimingSplit("Cleanup"); |
| |
| if (result) { |
| VLOG(compiler) << cu.instruction_set << ": Compiled " << PrettyMethod(method_idx, dex_file); |
| } else { |
| VLOG(compiler) << cu.instruction_set << ": Deferred " << PrettyMethod(method_idx, dex_file); |
| } |
| |
| if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) { |
| if (cu.arena.BytesAllocated() > (1 * 1024 *1024)) { |
| MemStats mem_stats(cu.arena.GetMemStats()); |
| LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats); |
| } |
| } |
| |
| if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) { |
| LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks() |
| << " " << PrettyMethod(method_idx, dex_file); |
| } |
| |
| cu.EndTiming(); |
| driver->GetTimingsLogger()->AddLogger(cu.timings); |
| return result; |
| } |
| |
| CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags, |
| uint32_t method_idx, |
| const DexFile& dex_file) const { |
| return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file); |
| } |
| |
| uintptr_t QuickCompiler::GetEntryPointOf(ArtMethod* method) const { |
| return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize( |
| InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet()))); |
| } |
| |
| Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) { |
| UNUSED(compilation_unit); |
| Mir2Lir* mir_to_lir = nullptr; |
| switch (cu->instruction_set) { |
| #ifdef ART_ENABLE_CODEGEN_arm |
| case kThumb2: |
| mir_to_lir = ArmCodeGenerator(cu, cu->mir_graph.get(), &cu->arena); |
| break; |
| #endif // ART_ENABLE_CODEGEN_arm |
| #ifdef ART_ENABLE_CODEGEN_arm64 |
| case kArm64: |
| mir_to_lir = Arm64CodeGenerator(cu, cu->mir_graph.get(), &cu->arena); |
| break; |
| #endif // ART_ENABLE_CODEGEN_arm64 |
| #if defined(ART_ENABLE_CODEGEN_mips) || defined(ART_ENABLE_CODEGEN_mips64) |
| // Intentional 2 level ifdef. Want to fail on mips64 if it is not enabled, even if mips is |
| // and vice versa. |
| #ifdef ART_ENABLE_CODEGEN_mips |
| case kMips: |
| // Fall-through. |
| #endif // ART_ENABLE_CODEGEN_mips |
| #ifdef ART_ENABLE_CODEGEN_mips64 |
| case kMips64: |
| #endif // ART_ENABLE_CODEGEN_mips64 |
| mir_to_lir = MipsCodeGenerator(cu, cu->mir_graph.get(), &cu->arena); |
| break; |
| #endif // ART_ENABLE_CODEGEN_mips || ART_ENABLE_CODEGEN_mips64 |
| #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) |
| // Intentional 2 level ifdef. Want to fail on x86_64 if it is not enabled, even if x86 is |
| // and vice versa. |
| #ifdef ART_ENABLE_CODEGEN_x86 |
| case kX86: |
| // Fall-through. |
| #endif // ART_ENABLE_CODEGEN_x86 |
| #ifdef ART_ENABLE_CODEGEN_x86_64 |
| case kX86_64: |
| #endif // ART_ENABLE_CODEGEN_x86_64 |
| mir_to_lir = X86CodeGenerator(cu, cu->mir_graph.get(), &cu->arena); |
| break; |
| #endif // ART_ENABLE_CODEGEN_x86 || ART_ENABLE_CODEGEN_x86_64 |
| default: |
| LOG(FATAL) << "Unexpected instruction set: " << cu->instruction_set; |
| } |
| |
| /* The number of compiler temporaries depends on backend so set it up now if possible */ |
| if (mir_to_lir) { |
| size_t max_temps = mir_to_lir->GetMaxPossibleCompilerTemps(); |
| bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps); |
| CHECK(set_max); |
| } |
| return mir_to_lir; |
| } |
| |
| QuickCompiler::QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) { |
| const auto& compiler_options = driver->GetCompilerOptions(); |
| auto* pass_manager_options = compiler_options.GetPassManagerOptions(); |
| pre_opt_pass_manager_.reset(new PassManager(*pass_manager_options)); |
| CHECK(pre_opt_pass_manager_.get() != nullptr); |
| PassDriverMEOpts::SetupPasses(pre_opt_pass_manager_.get()); |
| pre_opt_pass_manager_->CreateDefaultPassList(); |
| if (pass_manager_options->GetPrintPassOptions()) { |
| PassDriverMEOpts::PrintPassOptions(pre_opt_pass_manager_.get()); |
| } |
| // TODO: Different options for pre vs post opts? |
| post_opt_pass_manager_.reset(new PassManager(PassManagerOptions())); |
| CHECK(post_opt_pass_manager_.get() != nullptr); |
| PassDriverMEPostOpt::SetupPasses(post_opt_pass_manager_.get()); |
| post_opt_pass_manager_->CreateDefaultPassList(); |
| if (pass_manager_options->GetPrintPassOptions()) { |
| PassDriverMEPostOpt::PrintPassOptions(post_opt_pass_manager_.get()); |
| } |
| } |
| |
| QuickCompiler::~QuickCompiler() { |
| } |
| |
| Compiler* CreateQuickCompiler(CompilerDriver* driver) { |
| return QuickCompiler::Create(driver); |
| } |
| |
| Compiler* QuickCompiler::Create(CompilerDriver* driver) { |
| return new QuickCompiler(driver); |
| } |
| |
| } // namespace art |