Revert "Refactor HGraphBuilder and SsaBuilder to remove HLocals"
Bug: 27995065
This reverts commit e3ff7b293be2a6791fe9d135d660c0cffe4bd73f.
Change-Id: I5363c7ce18f47fd422c15eed5423a345a57249d8
diff --git a/compiler/Android.mk b/compiler/Android.mk
index e9c22d2..a7867e8 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -54,7 +54,6 @@
optimizing/induction_var_analysis.cc \
optimizing/induction_var_range.cc \
optimizing/inliner.cc \
- optimizing/instruction_builder.cc \
optimizing/instruction_simplifier.cc \
optimizing/intrinsics.cc \
optimizing/licm.cc \
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 86742e6..5315858 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -20,17 +20,110 @@
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
#include "base/logging.h"
+#include "bytecode_utils.h"
+#include "class_linker.h"
#include "dex/verified_method.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "dex/verified_method.h"
+#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "nodes.h"
#include "primitive.h"
+#include "scoped_thread_state_change.h"
+#include "ssa_builder.h"
#include "thread.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
+void HGraphBuilder::InitializeLocals(uint16_t count) {
+ graph_->SetNumberOfVRegs(count);
+ locals_.resize(count);
+ HBasicBlock* entry_block = graph_->GetEntryBlock();
+ for (int i = 0; i < count; i++) {
+ HLocal* local = new (arena_) HLocal(i);
+ entry_block->AddInstruction(local);
+ locals_[i] = local;
+ }
+}
+
+void HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
+ // dex_compilation_unit_ is null only when unit testing.
+ if (dex_compilation_unit_ == nullptr) {
+ return;
+ }
+
+ HBasicBlock* entry_block = graph_->GetEntryBlock();
+
+ graph_->SetNumberOfInVRegs(number_of_parameters);
+ const char* shorty = dex_compilation_unit_->GetShorty();
+ int locals_index = locals_.size() - number_of_parameters;
+ int parameter_index = 0;
+
+ const DexFile::MethodId& referrer_method_id =
+ dex_file_->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
+ if (!dex_compilation_unit_->IsStatic()) {
+ // Add the implicit 'this' argument, not expressed in the signature.
+ HParameterValue* parameter = new (arena_) HParameterValue(*dex_file_,
+ referrer_method_id.class_idx_,
+ parameter_index++,
+ Primitive::kPrimNot,
+ true);
+ entry_block->AddInstruction(parameter);
+ HLocal* local = GetLocalAt(locals_index++);
+ entry_block->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
+ number_of_parameters--;
+ }
+
+ const DexFile::ProtoId& proto = dex_file_->GetMethodPrototype(referrer_method_id);
+ const DexFile::TypeList* arg_types = dex_file_->GetProtoParameters(proto);
+ for (int i = 0, shorty_pos = 1; i < number_of_parameters; i++) {
+ HParameterValue* parameter = new (arena_) HParameterValue(
+ *dex_file_,
+ arg_types->GetTypeItem(shorty_pos - 1).type_idx_,
+ parameter_index++,
+ Primitive::GetType(shorty[shorty_pos]),
+ false);
+ ++shorty_pos;
+ entry_block->AddInstruction(parameter);
+ HLocal* local = GetLocalAt(locals_index++);
+ // Store the parameter value in the local that the dex code will use
+ // to reference that parameter.
+ entry_block->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
+ bool is_wide = (parameter->GetType() == Primitive::kPrimLong)
+ || (parameter->GetType() == Primitive::kPrimDouble);
+ if (is_wide) {
+ i++;
+ locals_index++;
+ parameter_index++;
+ }
+ }
+}
+
+template<typename T>
+void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ T* comparison = new (arena_) T(first, second, dex_pc);
+ current_block_->AddInstruction(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
+ current_block_->AddInstruction(ifinst);
+ current_block_ = nullptr;
+}
+
+template<typename T>
+void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+ T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
+ current_block_->AddInstruction(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
+ current_block_->AddInstruction(ifinst);
+ current_block_ = nullptr;
+}
+
void HGraphBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) {
if (compilation_stats_ != nullptr) {
compilation_stats_->RecordStat(compilation_stat);
@@ -70,13 +163,84 @@
return false;
}
-GraphAnalysisResult HGraphBuilder::BuildGraph() {
- DCHECK(graph_->GetBlocks().empty());
+static bool BlockIsNotPopulated(HBasicBlock* block) {
+ if (!block->GetPhis().IsEmpty()) {
+ return false;
+ } else if (block->IsLoopHeader()) {
+ // Suspend checks were inserted into loop headers during building of dominator tree.
+ DCHECK(block->GetFirstInstruction()->IsSuspendCheck());
+ return block->GetFirstInstruction() == block->GetLastInstruction();
+ } else {
+ return block->GetInstructions().IsEmpty();
+ }
+}
- graph_->SetNumberOfVRegs(code_item_.registers_size_);
- graph_->SetNumberOfInVRegs(code_item_.ins_size_);
+bool HGraphBuilder::GenerateInstructions() {
+ // Find locations where we want to generate extra stackmaps for native debugging.
+ // This allows us to generate the info only at interesting points (for example,
+ // at start of java statement) rather than before every dex instruction.
+ const bool native_debuggable = compiler_driver_ != nullptr &&
+ compiler_driver_->GetCompilerOptions().GetNativeDebuggable();
+ ArenaBitVector* native_debug_info_locations;
+ if (native_debuggable) {
+ const uint32_t num_instructions = code_item_.insns_size_in_code_units_;
+ native_debug_info_locations =
+ ArenaBitVector::Create(arena_, num_instructions, false, kArenaAllocGraphBuilder);
+ FindNativeDebugInfoLocations(native_debug_info_locations);
+ }
+
+ InitializeLocals(code_item_.registers_size_);
+ InitializeParameters(code_item_.ins_size_);
+
+ // Add the suspend check to the entry block.
+ current_block_ = graph_->GetEntryBlock();
+ current_block_->AddInstruction(new (arena_) HSuspendCheck(0));
+
+ for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
+ uint32_t dex_pc = it.CurrentDexPc();
+
+ HBasicBlock* next_block = FindBlockStartingAt(dex_pc);
+ if (next_block != nullptr && next_block->GetGraph() != nullptr) {
+ if (current_block_ != nullptr) {
+ // Branching instructions clear current_block, so we know
+ // the last instruction of the current block is not a branching
+ // instruction. We add an unconditional goto to the found block.
+ current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
+ }
+ DCHECK(BlockIsNotPopulated(next_block));
+ current_block_ = next_block;
+ }
+
+ if (current_block_ == nullptr) {
+ // Unreachable code.
+ continue;
+ }
+
+ if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
+ current_block_->AddInstruction(new (arena_) HNativeDebugInfo(dex_pc));
+ }
+
+ if (!AnalyzeDexInstruction(it.CurrentInstruction(), dex_pc)) {
+ return false;
+ }
+ }
+
+ // Add Exit to the exit block.
+ HBasicBlock* exit_block = graph_->GetExitBlock();
+ if (exit_block == nullptr) {
+ // Unreachable exit block was removed.
+ } else {
+ exit_block->AddInstruction(new (arena_) HExit());
+ }
+
+ return true;
+}
+
+GraphAnalysisResult HGraphBuilder::BuildGraph(StackHandleScopeCollection* handles) {
+ DCHECK(graph_->GetBlocks().empty());
graph_->SetMaximumNumberOfOutVRegs(code_item_.outs_size_);
graph_->SetHasTryCatch(code_item_.tries_size_ != 0);
+ graph_->InitializeInexactObjectRTI(handles);
// 1) Create basic blocks and link them together. Basic blocks are left
// unpopulated with the exception of synthetic blocks, e.g. HTryBoundaries.
@@ -97,12 +261,2264 @@
}
// 4) Populate basic blocks with instructions.
- if (!instruction_builder_.Build()) {
+ if (!GenerateInstructions()) {
return kAnalysisInvalidBytecode;
}
// 5) Type the graph and eliminate dead/redundant phis.
- return ssa_builder_.BuildSsa();
+ return SsaBuilder(graph_, code_item_, handles).BuildSsa();
+}
+
+void HGraphBuilder::FindNativeDebugInfoLocations(ArenaBitVector* locations) {
+ // The callback gets called when the line number changes.
+ // In other words, it marks the start of new java statement.
+ struct Callback {
+ static bool Position(void* ctx, const DexFile::PositionInfo& entry) {
+ static_cast<ArenaBitVector*>(ctx)->SetBit(entry.address_);
+ return false;
+ }
+ };
+ dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations);
+ // Instruction-specific tweaks.
+ const Instruction* const begin = Instruction::At(code_item_.insns_);
+ const Instruction* const end = begin->RelativeAt(code_item_.insns_size_in_code_units_);
+ for (const Instruction* inst = begin; inst < end; inst = inst->Next()) {
+ switch (inst->Opcode()) {
+ case Instruction::MOVE_EXCEPTION: {
+ // Stop in native debugger after the exception has been moved.
+ // The compiler also expects the move at the start of basic block so
+ // we do not want to interfere by inserting native-debug-info before it.
+ locations->ClearBit(inst->GetDexPc(code_item_.insns_));
+ const Instruction* next = inst->Next();
+ if (next < end) {
+ locations->SetBit(next->GetDexPc(code_item_.insns_));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+template<typename T>
+void HGraphBuilder::Unop_12x(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+void HGraphBuilder::Conversion_12x(const Instruction& instruction,
+ Primitive::Type input_type,
+ Primitive::Type result_type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), input_type, dex_pc);
+ current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+template<typename T>
+void HGraphBuilder::Binop_23x(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+template<typename T>
+void HGraphBuilder::Binop_23x_shift(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+void HGraphBuilder::Binop_23x_cmp(const Instruction& instruction,
+ Primitive::Type type,
+ ComparisonBias bias,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
+ current_block_->AddInstruction(new (arena_) HCompare(type, first, second, bias, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+template<typename T>
+void HGraphBuilder::Binop_12x_shift(const Instruction& instruction, Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+template<typename T>
+void HGraphBuilder::Binop_12x(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegB(), type, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+template<typename T>
+void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s(), dex_pc);
+ if (reverse) {
+ std::swap(first, second);
+ }
+ current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+template<typename T>
+void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b(), dex_pc);
+ if (reverse) {
+ std::swap(first, second);
+ }
+ current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+}
+
+static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const CompilerDriver& driver) {
+ Thread* self = Thread::Current();
+ return cu->IsConstructor()
+ && driver.RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
+}
+
+// Returns true if `block` has only one successor which starts at the next
+// dex_pc after `instruction` at `dex_pc`.
+static bool IsFallthroughInstruction(const Instruction& instruction,
+ uint32_t dex_pc,
+ HBasicBlock* block) {
+ uint32_t next_dex_pc = dex_pc + instruction.SizeInCodeUnits();
+ return block->GetSingleSuccessor()->GetDexPc() == next_dex_pc;
+}
+
+void HGraphBuilder::BuildSwitch(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+ DexSwitchTable table(instruction, dex_pc);
+
+ if (table.GetNumEntries() == 0) {
+ // Empty Switch. Code falls through to the next block.
+ DCHECK(IsFallthroughInstruction(instruction, dex_pc, current_block_));
+ current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
+ } else if (table.ShouldBuildDecisionTree()) {
+ for (DexSwitchTableIterator it(table); !it.Done(); it.Advance()) {
+ HInstruction* case_value = graph_->GetIntConstant(it.CurrentKey(), dex_pc);
+ HEqual* comparison = new (arena_) HEqual(value, case_value, dex_pc);
+ current_block_->AddInstruction(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
+ current_block_->AddInstruction(ifinst);
+
+ if (!it.IsLast()) {
+ current_block_ = FindBlockStartingAt(it.GetDexPcForCurrentIndex());
+ }
+ }
+ } else {
+ current_block_->AddInstruction(
+ new (arena_) HPackedSwitch(table.GetEntryAt(0), table.GetNumEntries(), value, dex_pc));
+ }
+
+ current_block_ = nullptr;
+}
+
+void HGraphBuilder::BuildReturn(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ if (type == Primitive::kPrimVoid) {
+ if (graph_->ShouldGenerateConstructorBarrier()) {
+ // The compilation unit is null during testing.
+ if (dex_compilation_unit_ != nullptr) {
+ DCHECK(RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_))
+ << "Inconsistent use of ShouldGenerateConstructorBarrier. Should not generate a barrier.";
+ }
+ current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore, dex_pc));
+ }
+ current_block_->AddInstruction(new (arena_) HReturnVoid(dex_pc));
+ } else {
+ HInstruction* value = LoadLocal(instruction.VRegA(), type, dex_pc);
+ current_block_->AddInstruction(new (arena_) HReturn(value, dex_pc));
+ }
+ current_block_ = nullptr;
+}
+
+static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) {
+ switch (opcode) {
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ return kStatic;
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ return kDirect;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ return kVirtual;
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ return kInterface;
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_SUPER:
+ return kSuper;
+ default:
+ LOG(FATAL) << "Unexpected invoke opcode: " << opcode;
+ UNREACHABLE();
+ }
+}
+
+ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+
+ ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
+
+ ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
+ *dex_compilation_unit_->GetDexFile(),
+ method_idx,
+ dex_compilation_unit_->GetDexCache(),
+ class_loader,
+ /* referrer */ nullptr,
+ invoke_type);
+
+ if (UNLIKELY(resolved_method == nullptr)) {
+ // Clean up any exception left by type resolution.
+ soa.Self()->ClearException();
+ return nullptr;
+ }
+
+ // Check access. The class linker has a fast path for looking into the dex cache
+ // and does not check the access if it hits it.
+ if (compiling_class.Get() == nullptr) {
+ if (!resolved_method->IsPublic()) {
+ return nullptr;
+ }
+ } else if (!compiling_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
+ resolved_method,
+ dex_compilation_unit_->GetDexCache().Get(),
+ method_idx)) {
+ return nullptr;
+ }
+
+ // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not.
+ // We need to look at the referrer's super class vtable. We need to do this to know if we need to
+ // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
+ // which require runtime handling.
+ if (invoke_type == kSuper) {
+ if (compiling_class.Get() == nullptr) {
+ // We could not determine the method's class we need to wait until runtime.
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ return nullptr;
+ }
+ ArtMethod* current_method = graph_->GetArtMethod();
+ DCHECK(current_method != nullptr);
+ Handle<mirror::Class> methods_class(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
+ method_idx,
+ current_method)));
+ if (methods_class.Get() == nullptr) {
+ // Invoking a super method requires knowing the actual super class. If we did not resolve
+ // the compiling method's declaring class (which only happens for ahead of time
+ // compilation), bail out.
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ return nullptr;
+ } else {
+ ArtMethod* actual_method;
+ if (methods_class->IsInterface()) {
+ actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
+ resolved_method, class_linker->GetImagePointerSize());
+ } else {
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
+ vtable_index, class_linker->GetImagePointerSize());
+ }
+ if (actual_method != resolved_method &&
+ !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+ // The back-end code generator relies on this check in order to ensure that it will not
+ // attempt to read the dex_cache with a dex_method_index that is not from the correct
+ // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
+ // builder, which means that the code-generator (and compiler driver during sharpening and
+ // inliner, maybe) might invoke an incorrect method.
+ // TODO: The actual method could still be referenced in the current dex file, so we
+ // could try locating it.
+ // TODO: Remove the dex_file restriction.
+ return nullptr;
+ }
+ if (!actual_method->IsInvokable()) {
+ // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
+ // could resolve the callee to the wrong method.
+ return nullptr;
+ }
+ resolved_method = actual_method;
+ }
+ }
+
+ // Check for incompatible class changes. The class linker has a fast path for
+ // looking into the dex cache and does not check incompatible class changes if it hits it.
+ if (resolved_method->CheckIncompatibleClassChange(invoke_type)) {
+ return nullptr;
+ }
+
+ return resolved_method;
+}
+
+bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
+ uint32_t dex_pc,
+ uint32_t method_idx,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index) {
+ InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
+ const char* descriptor = dex_file_->GetMethodShorty(method_idx);
+ Primitive::Type return_type = Primitive::GetType(descriptor[0]);
+
+ // Remove the return type from the 'proto'.
+ size_t number_of_arguments = strlen(descriptor) - 1;
+ if (invoke_type != kStatic) { // instance call
+ // One extra argument for 'this'.
+ number_of_arguments++;
+ }
+
+ MethodReference target_method(dex_file_, method_idx);
+
+ // Special handling for string init.
+ int32_t string_init_offset = 0;
+ bool is_string_init = compiler_driver_->IsStringInit(method_idx,
+ dex_file_,
+ &string_init_offset);
+ // Replace calls to String.<init> with StringFactory.
+ if (is_string_init) {
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+ HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ dchecked_integral_cast<uint64_t>(string_init_offset),
+ 0U
+ };
+ HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
+ arena_,
+ number_of_arguments - 1,
+ Primitive::kPrimNot /*return_type */,
+ dex_pc,
+ method_idx,
+ target_method,
+ dispatch_info,
+ invoke_type,
+ kStatic /* optimized_invoke_type */,
+ HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
+ return HandleStringInit(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor);
+ }
+
+ ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type);
+
+ if (UNLIKELY(resolved_method == nullptr)) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod);
+ HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ invoke_type);
+ return HandleInvoke(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ nullptr /* clinit_check */);
+ }
+
+ // Potential class initialization check, in the case of a static method call.
+ HClinitCheck* clinit_check = nullptr;
+ HInvoke* invoke = nullptr;
+ if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) {
+ // By default, consider that the called method implicitly requires
+ // an initialization check of its declaring method.
+ HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement
+ = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit;
+ ScopedObjectAccess soa(Thread::Current());
+ if (invoke_type == kStatic) {
+ clinit_check = ProcessClinitCheckForInvoke(
+ dex_pc, resolved_method, method_idx, &clinit_check_requirement);
+ } else if (invoke_type == kSuper) {
+ if (IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+ // Update the target method to the one resolved. Note that this may be a no-op if
+ // we resolved to the method referenced by the instruction.
+ method_idx = resolved_method->GetDexMethodIndex();
+ target_method = MethodReference(dex_file_, method_idx);
+ }
+ }
+
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0U
+ };
+ invoke = new (arena_) HInvokeStaticOrDirect(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ target_method,
+ dispatch_info,
+ invoke_type,
+ invoke_type,
+ clinit_check_requirement);
+ } else if (invoke_type == kVirtual) {
+ ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
+ invoke = new (arena_) HInvokeVirtual(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ resolved_method->GetMethodIndex());
+ } else {
+ DCHECK_EQ(invoke_type, kInterface);
+ ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
+ invoke = new (arena_) HInvokeInterface(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ resolved_method->GetDexMethodIndex());
+ }
+
+ return HandleInvoke(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ clinit_check);
+}
+
+bool HGraphBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) {
+ bool finalizable;
+ bool can_throw = NeedsAccessCheck(type_index, &finalizable);
+
+ // Only the non-resolved entrypoint handles the finalizable class case. If we
+ // need access checks, then we haven't resolved the method and the class may
+ // again be finalizable.
+ QuickEntrypointEnum entrypoint = (finalizable || can_throw)
+ ? kQuickAllocObject
+ : kQuickAllocObjectInitialized;
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
+
+ if (outer_dex_cache.Get() != dex_cache.Get()) {
+ // We currently do not support inlining allocations across dex files.
+ return false;
+ }
+
+ HLoadClass* load_class = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ type_index,
+ outer_dex_file,
+ IsOutermostCompilingClass(type_index),
+ dex_pc,
+ /*needs_access_check*/ can_throw,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, type_index));
+
+ current_block_->AddInstruction(load_class);
+ HInstruction* cls = load_class;
+ if (!IsInitialized(resolved_class)) {
+ cls = new (arena_) HClinitCheck(load_class, dex_pc);
+ current_block_->AddInstruction(cls);
+ }
+
+ current_block_->AddInstruction(new (arena_) HNewInstance(
+ cls,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ can_throw,
+ finalizable,
+ entrypoint));
+ return true;
+}
+
+static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class);
+}
+
+bool HGraphBuilder::IsInitialized(Handle<mirror::Class> cls) const {
+ if (cls.Get() == nullptr) {
+ return false;
+ }
+
+ // `CanAssumeClassIsLoaded` will return true if we're JITting, or will
+ // check whether the class is in an image for the AOT compilation.
+ if (cls->IsInitialized() &&
+ compiler_driver_->CanAssumeClassIsLoaded(cls.Get())) {
+ return true;
+ }
+
+ if (IsSubClass(GetOutermostCompilingClass(), cls.Get())) {
+ return true;
+ }
+
+ // TODO: We should walk over the inlined methods, but we don't pass
+ // that information to the builder.
+ if (IsSubClass(GetCompilingClass(), cls.Get())) {
+ return true;
+ }
+
+ return false;
+}
+
+HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke(
+ uint32_t dex_pc,
+ ArtMethod* resolved_method,
+ uint32_t method_idx,
+ HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Thread* self = Thread::Current();
+ StackHandleScope<4> hs(self);
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ self, *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(
+ self, outer_dex_file)));
+ Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
+ Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass()));
+
+ // The index at which the method's class is stored in the DexCache's type array.
+ uint32_t storage_index = DexFile::kDexNoIndex;
+ bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get());
+ if (is_outer_class) {
+ storage_index = outer_class->GetDexTypeIndex();
+ } else if (outer_dex_cache.Get() == dex_cache.Get()) {
+ // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer.
+ compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(),
+ GetCompilingClass(),
+ resolved_method,
+ method_idx,
+ &storage_index);
+ }
+
+ HClinitCheck* clinit_check = nullptr;
+
+ if (IsInitialized(resolved_method_class)) {
+ *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
+ } else if (storage_index != DexFile::kDexNoIndex) {
+ *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
+ HLoadClass* load_class = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ storage_index,
+ outer_dex_file,
+ is_outer_class,
+ dex_pc,
+ /*needs_access_check*/ false,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index));
+ current_block_->AddInstruction(load_class);
+ clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
+ current_block_->AddInstruction(clinit_check);
+ }
+ return clinit_check;
+}
+
+bool HGraphBuilder::SetupInvokeArguments(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ size_t start_index,
+ size_t* argument_index) {
+ uint32_t descriptor_index = 1; // Skip the return type.
+ uint32_t dex_pc = invoke->GetDexPc();
+
+ for (size_t i = start_index;
+ // Make sure we don't go over the expected arguments or over the number of
+ // dex registers given. If the instruction was seen as dead by the verifier,
+ // it hasn't been properly checked.
+ (i < number_of_vreg_arguments) && (*argument_index < invoke->GetNumberOfArguments());
+ i++, (*argument_index)++) {
+ Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
+ bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
+ if (!is_range
+ && is_wide
+ && ((i + 1 == number_of_vreg_arguments) || (args[i] + 1 != args[i + 1]))) {
+ // Longs and doubles should be in pairs, that is, sequential registers. The verifier should
+ // reject any class where this is violated. However, the verifier only does these checks
+ // on non trivially dead instructions, so we just bailout the compilation.
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because of non-sequential dex register pair in wide argument";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
+ return false;
+ }
+ HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
+ invoke->SetArgumentAt(*argument_index, arg);
+ if (is_wide) {
+ i++;
+ }
+ }
+
+ if (*argument_index != invoke->GetNumberOfArguments()) {
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because of wrong number of arguments in invoke instruction";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
+ return false;
+ }
+
+ if (invoke->IsInvokeStaticOrDirect() &&
+ HInvokeStaticOrDirect::NeedsCurrentMethodInput(
+ invoke->AsInvokeStaticOrDirect()->GetMethodLoadKind())) {
+ invoke->SetArgumentAt(*argument_index, graph_->GetCurrentMethod());
+ (*argument_index)++;
+ }
+
+ return true;
+}
+
+bool HGraphBuilder::HandleInvoke(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ HClinitCheck* clinit_check) {
+ DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit());
+
+ size_t start_index = 0;
+ size_t argument_index = 0;
+ if (invoke->GetOriginalInvokeType() != InvokeType::kStatic) { // Instance call.
+ HInstruction* arg = LoadLocal(
+ is_range ? register_index : args[0], Primitive::kPrimNot, invoke->GetDexPc());
+ HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
+ current_block_->AddInstruction(null_check);
+ invoke->SetArgumentAt(0, null_check);
+ start_index = 1;
+ argument_index = 1;
+ }
+
+ if (!SetupInvokeArguments(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ start_index,
+ &argument_index)) {
+ return false;
+ }
+
+ if (clinit_check != nullptr) {
+ // Add the class initialization check as last input of `invoke`.
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ DCHECK(invoke->AsInvokeStaticOrDirect()->GetClinitCheckRequirement()
+ == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit);
+ invoke->SetArgumentAt(argument_index, clinit_check);
+ argument_index++;
+ }
+
+ current_block_->AddInstruction(invoke);
+ latest_result_ = invoke;
+
+ return true;
+}
+
+bool HGraphBuilder::HandleStringInit(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor) {
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit());
+
+ size_t start_index = 1;
+ size_t argument_index = 0;
+ if (!SetupInvokeArguments(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ start_index,
+ &argument_index)) {
+ return false;
+ }
+
+ // Add move-result for StringFactory method.
+ uint32_t orig_this_reg = is_range ? register_index : args[0];
+ HInstruction* new_instance = LoadLocal(orig_this_reg, Primitive::kPrimNot, invoke->GetDexPc());
+ invoke->SetArgumentAt(argument_index, new_instance);
+ current_block_->AddInstruction(invoke);
+
+ latest_result_ = invoke;
+ return true;
+}
+
+static Primitive::Type GetFieldAccessType(const DexFile& dex_file, uint16_t field_index) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+ return Primitive::GetType(type[0]);
+}
+
+bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put) {
+ uint32_t source_or_dest_reg = instruction.VRegA_22c();
+ uint32_t obj_reg = instruction.VRegB_22c();
+ uint16_t field_index;
+ if (instruction.IsQuickened()) {
+ if (!CanDecodeQuickenedInfo()) {
+ return false;
+ }
+ field_index = LookupQuickenedInfo(dex_pc);
+ } else {
+ field_index = instruction.VRegC_22c();
+ }
+
+ ScopedObjectAccess soa(Thread::Current());
+ ArtField* resolved_field =
+ compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa);
+
+
+ HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot, dex_pc);
+ HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc);
+ current_block_->AddInstruction(null_check);
+
+ Primitive::Type field_type = (resolved_field == nullptr)
+ ? GetFieldAccessType(*dex_file_, field_index)
+ : resolved_field->GetTypeAsPrimitiveType();
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
+ HInstruction* field_set = nullptr;
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check,
+ value,
+ field_type,
+ field_index,
+ dex_pc);
+ } else {
+ uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
+ field_set = new (arena_) HInstanceFieldSet(null_check,
+ value,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc);
+ }
+ current_block_->AddInstruction(field_set);
+ } else {
+ HInstruction* field_get = nullptr;
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check,
+ field_type,
+ field_index,
+ dex_pc);
+ } else {
+ uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
+ field_get = new (arena_) HInstanceFieldGet(null_check,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc);
+ }
+ current_block_->AddInstruction(field_get);
+ UpdateLocal(source_or_dest_reg, field_get, dex_pc);
+ }
+
+ return true;
+}
+
+static mirror::Class* GetClassFrom(CompilerDriver* driver,
+ const DexCompilationUnit& compilation_unit) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ const DexFile& dex_file = *compilation_unit.GetDexFile();
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader())));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ compilation_unit.GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
+
+ return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
+}
+
+mirror::Class* HGraphBuilder::GetOutermostCompilingClass() const {
+ return GetClassFrom(compiler_driver_, *outer_compilation_unit_);
+}
+
+mirror::Class* HGraphBuilder::GetCompilingClass() const {
+ return GetClassFrom(compiler_driver_, *dex_compilation_unit_);
+}
+
+bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
+ soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
+ Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+ // GetOutermostCompilingClass returns null when the class is unresolved
+ // (e.g. if it derives from an unresolved class). This is bogus knowing that
+ // we are compiling it.
+ // When this happens we cannot establish a direct relation between the current
+ // class and the outer class, so we return false.
+ // (Note that this is only used for optimizing invokes and field accesses)
+ return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get());
+}
+
+void HGraphBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type field_type) {
+ uint32_t source_or_dest_reg = instruction.VRegA_21c();
+ uint16_t field_index = instruction.VRegB_21c();
+
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
+ current_block_->AddInstruction(
+ new (arena_) HUnresolvedStaticFieldSet(value, field_type, field_index, dex_pc));
+ } else {
+ current_block_->AddInstruction(
+ new (arena_) HUnresolvedStaticFieldGet(field_type, field_index, dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
+ }
+}
+bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put) {
+ uint32_t source_or_dest_reg = instruction.VRegA_21c();
+ uint16_t field_index = instruction.VRegB_21c();
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<5> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ ArtField* resolved_field = compiler_driver_->ResolveField(
+ soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
+
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ Primitive::Type field_type = GetFieldAccessType(*dex_file_, field_index);
+ BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+ return true;
+ }
+
+ Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
+ Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+ // The index at which the field's class is stored in the DexCache's type array.
+ uint32_t storage_index;
+ bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass());
+ if (is_outer_class) {
+ storage_index = outer_class->GetDexTypeIndex();
+ } else if (outer_dex_cache.Get() != dex_cache.Get()) {
+ // The compiler driver cannot currently understand multiple dex caches involved. Just bailout.
+ return false;
+ } else {
+ // TODO: This is rather expensive. Perf it and cache the results if needed.
+ std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
+ outer_dex_cache.Get(),
+ GetCompilingClass(),
+ resolved_field,
+ field_index,
+ &storage_index);
+ bool can_easily_access = is_put ? pair.second : pair.first;
+ if (!can_easily_access) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedFieldNotAFastAccess);
+ BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+ return true;
+ }
+ }
+
+ bool is_in_cache =
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index);
+ HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
+ storage_index,
+ outer_dex_file,
+ is_outer_class,
+ dex_pc,
+ /*needs_access_check*/ false,
+ is_in_cache);
+ current_block_->AddInstruction(constant);
+
+ HInstruction* cls = constant;
+
+ Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass()));
+ if (!IsInitialized(klass)) {
+ cls = new (arena_) HClinitCheck(constant, dex_pc);
+ current_block_->AddInstruction(cls);
+ }
+
+ uint16_t class_def_index = klass->GetDexClassDefIndex();
+ if (is_put) {
+ // We need to keep the class alive before loading the value.
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
+ DCHECK_EQ(value->GetType(), field_type);
+ current_block_->AddInstruction(new (arena_) HStaticFieldSet(cls,
+ value,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_cache_,
+ dex_pc));
+ } else {
+ current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_cache_,
+ dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
+ }
+ return true;
+}
+
+void HGraphBuilder::BuildCheckedDivRem(uint16_t out_vreg,
+ uint16_t first_vreg,
+ int64_t second_vreg_or_constant,
+ uint32_t dex_pc,
+ Primitive::Type type,
+ bool second_is_constant,
+ bool isDiv) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ HInstruction* first = LoadLocal(first_vreg, type, dex_pc);
+ HInstruction* second = nullptr;
+ if (second_is_constant) {
+ if (type == Primitive::kPrimInt) {
+ second = graph_->GetIntConstant(second_vreg_or_constant, dex_pc);
+ } else {
+ second = graph_->GetLongConstant(second_vreg_or_constant, dex_pc);
+ }
+ } else {
+ second = LoadLocal(second_vreg_or_constant, type, dex_pc);
+ }
+
+ if (!second_is_constant
+ || (type == Primitive::kPrimInt && second->AsIntConstant()->GetValue() == 0)
+ || (type == Primitive::kPrimLong && second->AsLongConstant()->GetValue() == 0)) {
+ second = new (arena_) HDivZeroCheck(second, dex_pc);
+ current_block_->AddInstruction(second);
+ }
+
+ if (isDiv) {
+ current_block_->AddInstruction(new (arena_) HDiv(type, first, second, dex_pc));
+ } else {
+ current_block_->AddInstruction(new (arena_) HRem(type, first, second, dex_pc));
+ }
+ UpdateLocal(out_vreg, current_block_->GetLastInstruction(), dex_pc);
+}
+
+void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type anticipated_type) {
+ uint8_t source_or_dest_reg = instruction.VRegA_23x();
+ uint8_t array_reg = instruction.VRegB_23x();
+ uint8_t index_reg = instruction.VRegC_23x();
+
+ HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot, dex_pc);
+ object = new (arena_) HNullCheck(object, dex_pc);
+ current_block_->AddInstruction(object);
+
+ HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
+ current_block_->AddInstruction(length);
+ HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt, dex_pc);
+ index = new (arena_) HBoundsCheck(index, length, dex_pc);
+ current_block_->AddInstruction(index);
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type, dex_pc);
+ // TODO: Insert a type check node if the type is Object.
+ current_block_->AddInstruction(new (arena_) HArraySet(
+ object, index, value, anticipated_type, dex_pc));
+ } else {
+ current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type, dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
+ }
+ graph_->SetHasBoundsChecks(true);
+}
+
+void HGraphBuilder::BuildFilledNewArray(uint32_t dex_pc,
+ uint32_t type_index,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index) {
+ HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
+ bool finalizable;
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
+ HInstruction* object = new (arena_) HNewArray(length,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ entrypoint);
+ current_block_->AddInstruction(object);
+
+ const char* descriptor = dex_file_->StringByTypeIdx(type_index);
+ DCHECK_EQ(descriptor[0], '[') << descriptor;
+ char primitive = descriptor[1];
+ DCHECK(primitive == 'I'
+ || primitive == 'L'
+ || primitive == '[') << descriptor;
+ bool is_reference_array = (primitive == 'L') || (primitive == '[');
+ Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt;
+
+ for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
+ HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ current_block_->AddInstruction(
+ new (arena_) HArraySet(object, index, value, type, dex_pc));
+ }
+ latest_result_ = object;
+}
+
+template <typename T>
+void HGraphBuilder::BuildFillArrayData(HInstruction* object,
+ const T* data,
+ uint32_t element_count,
+ Primitive::Type anticipated_type,
+ uint32_t dex_pc) {
+ for (uint32_t i = 0; i < element_count; ++i) {
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ HInstruction* value = graph_->GetIntConstant(data[i], dex_pc);
+ current_block_->AddInstruction(new (arena_) HArraySet(
+ object, index, value, anticipated_type, dex_pc));
+ }
+}
+
+void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot, dex_pc);
+ HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
+ current_block_->AddInstruction(null_check);
+
+ HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
+ current_block_->AddInstruction(length);
+
+ int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
+ const Instruction::ArrayDataPayload* payload =
+ reinterpret_cast<const Instruction::ArrayDataPayload*>(code_start_ + payload_offset);
+ const uint8_t* data = payload->data;
+ uint32_t element_count = payload->element_count;
+
+ // Implementation of this DEX instruction seems to be that the bounds check is
+ // done before doing any stores.
+ HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1, dex_pc);
+ current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc));
+
+ switch (payload->element_width) {
+ case 1:
+ BuildFillArrayData(null_check,
+ reinterpret_cast<const int8_t*>(data),
+ element_count,
+ Primitive::kPrimByte,
+ dex_pc);
+ break;
+ case 2:
+ BuildFillArrayData(null_check,
+ reinterpret_cast<const int16_t*>(data),
+ element_count,
+ Primitive::kPrimShort,
+ dex_pc);
+ break;
+ case 4:
+ BuildFillArrayData(null_check,
+ reinterpret_cast<const int32_t*>(data),
+ element_count,
+ Primitive::kPrimInt,
+ dex_pc);
+ break;
+ case 8:
+ BuildFillWideArrayData(null_check,
+ reinterpret_cast<const int64_t*>(data),
+ element_count,
+ dex_pc);
+ break;
+ default:
+ LOG(FATAL) << "Unknown element width for " << payload->element_width;
+ }
+ graph_->SetHasBoundsChecks(true);
+}
+
+void HGraphBuilder::BuildFillWideArrayData(HInstruction* object,
+ const int64_t* data,
+ uint32_t element_count,
+ uint32_t dex_pc) {
+ for (uint32_t i = 0; i < element_count; ++i) {
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ HInstruction* value = graph_->GetLongConstant(data[i], dex_pc);
+ current_block_->AddInstruction(new (arena_) HArraySet(
+ object, index, value, Primitive::kPrimLong, dex_pc));
+ }
+}
+
+static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (cls.Get() == nullptr) {
+ return TypeCheckKind::kUnresolvedCheck;
+ } else if (cls->IsInterface()) {
+ return TypeCheckKind::kInterfaceCheck;
+ } else if (cls->IsArrayClass()) {
+ if (cls->GetComponentType()->IsObjectClass()) {
+ return TypeCheckKind::kArrayObjectCheck;
+ } else if (cls->CannotBeAssignedFromOtherTypes()) {
+ return TypeCheckKind::kExactCheck;
+ } else {
+ return TypeCheckKind::kArrayCheck;
+ }
+ } else if (cls->IsFinal()) {
+ return TypeCheckKind::kExactCheck;
+ } else if (cls->IsAbstract()) {
+ return TypeCheckKind::kAbstractClassCheck;
+ } else {
+ return TypeCheckKind::kClassHierarchyCheck;
+ }
+}
+
+void HGraphBuilder::BuildTypeCheck(const Instruction& instruction,
+ uint8_t destination,
+ uint8_t reference,
+ uint16_t type_index,
+ uint32_t dex_pc) {
+ bool type_known_final, type_known_abstract, use_declaring_class;
+ bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(),
+ *dex_compilation_unit_->GetDexFile(),
+ type_index,
+ &type_known_final,
+ &type_known_abstract,
+ &use_declaring_class);
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
+ Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+
+ HInstruction* object = LoadLocal(reference, Primitive::kPrimNot, dex_pc);
+ HLoadClass* cls = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ type_index,
+ dex_file,
+ IsOutermostCompilingClass(type_index),
+ dex_pc,
+ !can_access,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index));
+ current_block_->AddInstruction(cls);
+
+ TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
+ if (instruction.Opcode() == Instruction::INSTANCE_OF) {
+ current_block_->AddInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc));
+ UpdateLocal(destination, current_block_->GetLastInstruction(), dex_pc);
+ } else {
+ DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
+ // We emit a CheckCast followed by a BoundType. CheckCast is a statement
+ // which may throw. If it succeeds BoundType sets the new type of `object`
+ // for all subsequent uses.
+ current_block_->AddInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc));
+ current_block_->AddInstruction(new (arena_) HBoundType(object, dex_pc));
+ UpdateLocal(reference, current_block_->GetLastInstruction(), dex_pc);
+ }
+}
+
+bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const {
+ return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, finalizable);
+}
+
+bool HGraphBuilder::CanDecodeQuickenedInfo() const {
+ return interpreter_metadata_ != nullptr;
+}
+
+uint16_t HGraphBuilder::LookupQuickenedInfo(uint32_t dex_pc) {
+ DCHECK(interpreter_metadata_ != nullptr);
+ uint32_t dex_pc_in_map = DecodeUnsignedLeb128(&interpreter_metadata_);
+ DCHECK_EQ(dex_pc, dex_pc_in_map);
+ return DecodeUnsignedLeb128(&interpreter_metadata_);
+}
+
+bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_pc) {
+ switch (instruction.Opcode()) {
+ case Instruction::CONST_4: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_16: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ case Instruction::CONST: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_16: {
+ int32_t register_index = instruction.VRegA();
+ // Get 16 bits of constant value, sign extended to 64 bits.
+ int64_t value = instruction.VRegB_21s();
+ value <<= 48;
+ value >>= 48;
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_32: {
+ int32_t register_index = instruction.VRegA();
+ // Get 32 bits of constant value, sign extended to 64 bits.
+ int64_t value = instruction.VRegB_31i();
+ value <<= 32;
+ value >>= 32;
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_WIDE: {
+ int32_t register_index = instruction.VRegA();
+ HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
+ break;
+ }
+
+ // Note that the SSA building will refine the types.
+ case Instruction::MOVE:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_16: {
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ UpdateLocal(instruction.VRegA(), value, dex_pc);
+ break;
+ }
+
+ // Note that the SSA building will refine the types.
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_FROM16:
+ case Instruction::MOVE_WIDE_16: {
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong, dex_pc);
+ UpdateLocal(instruction.VRegA(), value, dex_pc);
+ break;
+ }
+
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_OBJECT_FROM16: {
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot, dex_pc);
+ UpdateLocal(instruction.VRegA(), value, dex_pc);
+ break;
+ }
+
+ case Instruction::RETURN_VOID_NO_BARRIER:
+ case Instruction::RETURN_VOID: {
+ BuildReturn(instruction, Primitive::kPrimVoid, dex_pc);
+ break;
+ }
+
+#define IF_XX(comparison, cond) \
+ case Instruction::IF_##cond: If_22t<comparison>(instruction, dex_pc); break; \
+ case Instruction::IF_##cond##Z: If_21t<comparison>(instruction, dex_pc); break
+
+ IF_XX(HEqual, EQ);
+ IF_XX(HNotEqual, NE);
+ IF_XX(HLessThan, LT);
+ IF_XX(HLessThanOrEqual, LE);
+ IF_XX(HGreaterThan, GT);
+ IF_XX(HGreaterThanOrEqual, GE);
+
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32: {
+ current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
+ current_block_ = nullptr;
+ break;
+ }
+
+ case Instruction::RETURN: {
+ BuildReturn(instruction, return_type_, dex_pc);
+ break;
+ }
+
+ case Instruction::RETURN_OBJECT: {
+ BuildReturn(instruction, return_type_, dex_pc);
+ break;
+ }
+
+ case Instruction::RETURN_WIDE: {
+ BuildReturn(instruction, return_type_, dex_pc);
+ break;
+ }
+
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_QUICK: {
+ uint16_t method_idx;
+ if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_QUICK) {
+ if (!CanDecodeQuickenedInfo()) {
+ return false;
+ }
+ method_idx = LookupQuickenedInfo(dex_pc);
+ } else {
+ method_idx = instruction.VRegB_35c();
+ }
+ uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
+ uint32_t args[5];
+ instruction.GetVarArgs(args);
+ if (!BuildInvoke(instruction, dex_pc, method_idx,
+ number_of_vreg_arguments, false, args, -1)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_STATIC_RANGE:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+ uint16_t method_idx;
+ if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK) {
+ if (!CanDecodeQuickenedInfo()) {
+ return false;
+ }
+ method_idx = LookupQuickenedInfo(dex_pc);
+ } else {
+ method_idx = instruction.VRegB_3rc();
+ }
+ uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
+ uint32_t register_index = instruction.VRegC();
+ if (!BuildInvoke(instruction, dex_pc, method_idx,
+ number_of_vreg_arguments, true, nullptr, register_index)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::NEG_INT: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::NEG_LONG: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::NEG_FLOAT: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::NEG_DOUBLE: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::NOT_INT: {
+ Unop_12x<HNot>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::NOT_LONG: {
+ Unop_12x<HNot>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::LONG_TO_INT: {
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::LONG_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::LONG_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_INT: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_INT: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_BYTE: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_SHORT: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_CHAR: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_LONG: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_DOUBLE: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_FLOAT: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_INT: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_LONG: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_FLOAT: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_DOUBLE: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_LONG: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_FLOAT: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_DOUBLE: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_INT: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, false, true);
+ break;
+ }
+
+ case Instruction::DIV_LONG: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimLong, false, true);
+ break;
+ }
+
+ case Instruction::DIV_FLOAT: {
+ Binop_23x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_DOUBLE: {
+ Binop_23x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_INT: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, false, false);
+ break;
+ }
+
+ case Instruction::REM_LONG: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimLong, false, false);
+ break;
+ }
+
+ case Instruction::REM_FLOAT: {
+ Binop_23x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_DOUBLE: {
+ Binop_23x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT: {
+ Binop_23x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_LONG: {
+ Binop_23x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_INT: {
+ Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_LONG: {
+ Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_INT: {
+ Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_LONG: {
+ Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_INT: {
+ Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_LONG: {
+ Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT: {
+ Binop_23x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_LONG: {
+ Binop_23x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT: {
+ Binop_23x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_LONG: {
+ Binop_23x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_LONG_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_DOUBLE_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_FLOAT_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_INT_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_LONG_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_FLOAT_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_DOUBLE_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_LONG_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_FLOAT_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_DOUBLE_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_INT_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimInt, false, true);
+ break;
+ }
+
+ case Instruction::DIV_LONG_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimLong, false, true);
+ break;
+ }
+
+ case Instruction::REM_INT_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimInt, false, false);
+ break;
+ }
+
+ case Instruction::REM_LONG_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimLong, false, false);
+ break;
+ }
+
+ case Instruction::REM_FLOAT_2ADDR: {
+ Binop_12x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_DOUBLE_2ADDR: {
+ Binop_12x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_INT_2ADDR: {
+ Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_LONG_2ADDR: {
+ Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_INT_2ADDR: {
+ Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_LONG_2ADDR: {
+ Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_INT_2ADDR: {
+ Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_LONG_2ADDR: {
+ Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_FLOAT_2ADDR: {
+ Binop_12x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_DOUBLE_2ADDR: {
+ Binop_12x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT_2ADDR: {
+ Binop_12x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_LONG_2ADDR: {
+ Binop_12x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT_2ADDR: {
+ Binop_12x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_LONG_2ADDR: {
+ Binop_12x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT_2ADDR: {
+ Binop_12x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_LONG_2ADDR: {
+ Binop_12x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT16: {
+ Binop_22s<HAdd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT_LIT16: {
+ Binop_22s<HAnd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT_LIT16: {
+ Binop_22s<HOr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT_LIT16: {
+ Binop_22s<HXor>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::RSUB_INT: {
+ Binop_22s<HSub>(instruction, true, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT_LIT16: {
+ Binop_22s<HMul>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT8: {
+ Binop_22b<HAdd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT_LIT8: {
+ Binop_22b<HAnd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT_LIT8: {
+ Binop_22b<HOr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT_LIT8: {
+ Binop_22b<HXor>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::RSUB_INT_LIT8: {
+ Binop_22b<HSub>(instruction, true, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT_LIT8: {
+ Binop_22b<HMul>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::DIV_INT_LIT8: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, true, true);
+ break;
+ }
+
+ case Instruction::REM_INT_LIT16:
+ case Instruction::REM_INT_LIT8: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, true, false);
+ break;
+ }
+
+ case Instruction::SHL_INT_LIT8: {
+ Binop_22b<HShl>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_INT_LIT8: {
+ Binop_22b<HShr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_INT_LIT8: {
+ Binop_22b<HUShr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::NEW_INSTANCE: {
+ if (!BuildNewInstance(instruction.VRegB_21c(), dex_pc)) {
+ return false;
+ }
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
+ break;
+ }
+
+ case Instruction::NEW_ARRAY: {
+ uint16_t type_index = instruction.VRegC_22c();
+ HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt, dex_pc);
+ bool finalizable;
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
+ current_block_->AddInstruction(new (arena_) HNewArray(length,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ entrypoint));
+ UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction(), dex_pc);
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY: {
+ uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
+ uint32_t type_index = instruction.VRegB_35c();
+ uint32_t args[5];
+ instruction.GetVarArgs(args);
+ BuildFilledNewArray(dex_pc, type_index, number_of_vreg_arguments, false, args, 0);
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
+ uint32_t type_index = instruction.VRegB_3rc();
+ uint32_t register_index = instruction.VRegC_3rc();
+ BuildFilledNewArray(
+ dex_pc, type_index, number_of_vreg_arguments, true, nullptr, register_index);
+ break;
+ }
+
+ case Instruction::FILL_ARRAY_DATA: {
+ BuildFillArrayData(instruction, dex_pc);
+ break;
+ }
+
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_WIDE:
+ case Instruction::MOVE_RESULT_OBJECT: {
+ if (latest_result_ == nullptr) {
+ // Only dead code can lead to this situation, where the verifier
+ // does not reject the method.
+ } else {
+ // An Invoke/FilledNewArray and its MoveResult could have landed in
+ // different blocks if there was a try/catch block boundary between
+ // them. For Invoke, we insert a StoreLocal after the instruction. For
+ // FilledNewArray, the local needs to be updated after the array was
+ // filled, otherwise we might overwrite an input vreg.
+ HStoreLocal* update_local =
+ new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_, dex_pc);
+ HBasicBlock* block = latest_result_->GetBlock();
+ if (block == current_block_) {
+ // MoveResult and the previous instruction are in the same block.
+ current_block_->AddInstruction(update_local);
+ } else {
+ // The two instructions are in different blocks. Insert the MoveResult
+ // before the final control-flow instruction of the previous block.
+ DCHECK(block->EndsWithControlFlowInstruction());
+ DCHECK(current_block_->GetInstructions().IsEmpty());
+ block->InsertInstructionBefore(update_local, block->GetLastInstruction());
+ }
+ latest_result_ = nullptr;
+ }
+ break;
+ }
+
+ case Instruction::CMP_LONG: {
+ Binop_23x_cmp(instruction, Primitive::kPrimLong, ComparisonBias::kNoBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPG_FLOAT: {
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kGtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPG_DOUBLE: {
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kGtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPL_FLOAT: {
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kLtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPL_DOUBLE: {
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kLtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::NOP:
+ break;
+
+ case Instruction::IGET:
+ case Instruction::IGET_QUICK:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_WIDE_QUICK:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_OBJECT_QUICK:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BOOLEAN_QUICK:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_BYTE_QUICK:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_CHAR_QUICK:
+ case Instruction::IGET_SHORT:
+ case Instruction::IGET_SHORT_QUICK: {
+ if (!BuildInstanceFieldAccess(instruction, dex_pc, false)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::IPUT:
+ case Instruction::IPUT_QUICK:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_WIDE_QUICK:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_OBJECT_QUICK:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BOOLEAN_QUICK:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_BYTE_QUICK:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_CHAR_QUICK:
+ case Instruction::IPUT_SHORT:
+ case Instruction::IPUT_SHORT_QUICK: {
+ if (!BuildInstanceFieldAccess(instruction, dex_pc, true)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::SGET:
+ case Instruction::SGET_WIDE:
+ case Instruction::SGET_OBJECT:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT: {
+ if (!BuildStaticFieldAccess(instruction, dex_pc, false)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::SPUT:
+ case Instruction::SPUT_WIDE:
+ case Instruction::SPUT_OBJECT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT: {
+ if (!BuildStaticFieldAccess(instruction, dex_pc, true)) {
+ return false;
+ }
+ break;
+ }
+
+#define ARRAY_XX(kind, anticipated_type) \
+ case Instruction::AGET##kind: { \
+ BuildArrayAccess(instruction, dex_pc, false, anticipated_type); \
+ break; \
+ } \
+ case Instruction::APUT##kind: { \
+ BuildArrayAccess(instruction, dex_pc, true, anticipated_type); \
+ break; \
+ }
+
+ ARRAY_XX(, Primitive::kPrimInt);
+ ARRAY_XX(_WIDE, Primitive::kPrimLong);
+ ARRAY_XX(_OBJECT, Primitive::kPrimNot);
+ ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean);
+ ARRAY_XX(_BYTE, Primitive::kPrimByte);
+ ARRAY_XX(_CHAR, Primitive::kPrimChar);
+ ARRAY_XX(_SHORT, Primitive::kPrimShort);
+
+ case Instruction::ARRAY_LENGTH: {
+ HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot, dex_pc);
+ object = new (arena_) HNullCheck(object, dex_pc);
+ current_block_->AddInstruction(object);
+ current_block_->AddInstruction(new (arena_) HArrayLength(object, dex_pc));
+ UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction(), dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_STRING: {
+ uint32_t string_index = instruction.VRegB_21c();
+ current_block_->AddInstruction(
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
+ UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_STRING_JUMBO: {
+ uint32_t string_index = instruction.VRegB_31c();
+ current_block_->AddInstruction(
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
+ UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
+ break;
+ }
+
+ case Instruction::CONST_CLASS: {
+ uint16_t type_index = instruction.VRegB_21c();
+ bool type_known_final;
+ bool type_known_abstract;
+ bool dont_use_is_referrers_class;
+ // `CanAccessTypeWithoutChecks` will tell whether the method being
+ // built is trying to access its own class, so that the generated
+ // code can optimize for this case. However, the optimization does not
+ // work for inlining, so we use `IsOutermostCompilingClass` instead.
+ bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
+ &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
+ current_block_->AddInstruction(new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ type_index,
+ *dex_file_,
+ IsOutermostCompilingClass(type_index),
+ dex_pc,
+ !can_access,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(*dex_file_, type_index)));
+ UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
+ break;
+ }
+
+ case Instruction::MOVE_EXCEPTION: {
+ current_block_->AddInstruction(new (arena_) HLoadException(dex_pc));
+ UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction(), dex_pc);
+ current_block_->AddInstruction(new (arena_) HClearException(dex_pc));
+ break;
+ }
+
+ case Instruction::THROW: {
+ HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc);
+ current_block_->AddInstruction(new (arena_) HThrow(exception, dex_pc));
+ // We finished building this block. Set the current block to null to avoid
+ // adding dead instructions to it.
+ current_block_ = nullptr;
+ break;
+ }
+
+ case Instruction::INSTANCE_OF: {
+ uint8_t destination = instruction.VRegA_22c();
+ uint8_t reference = instruction.VRegB_22c();
+ uint16_t type_index = instruction.VRegC_22c();
+ BuildTypeCheck(instruction, destination, reference, type_index, dex_pc);
+ break;
+ }
+
+ case Instruction::CHECK_CAST: {
+ uint8_t reference = instruction.VRegA_21c();
+ uint16_t type_index = instruction.VRegB_21c();
+ BuildTypeCheck(instruction, -1, reference, type_index, dex_pc);
+ break;
+ }
+
+ case Instruction::MONITOR_ENTER: {
+ current_block_->AddInstruction(new (arena_) HMonitorOperation(
+ LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
+ HMonitorOperation::OperationKind::kEnter,
+ dex_pc));
+ break;
+ }
+
+ case Instruction::MONITOR_EXIT: {
+ current_block_->AddInstruction(new (arena_) HMonitorOperation(
+ LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
+ HMonitorOperation::OperationKind::kExit,
+ dex_pc));
+ break;
+ }
+
+ case Instruction::SPARSE_SWITCH:
+ case Instruction::PACKED_SWITCH: {
+ BuildSwitch(instruction, dex_pc);
+ break;
+ }
+
+ default:
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because of unhandled instruction "
+ << instruction.Name();
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnhandledInstruction);
+ return false;
+ }
+ return true;
+} // NOLINT(readability/fn_size)
+
+HLocal* HGraphBuilder::GetLocalAt(uint32_t register_index) const {
+ return locals_[register_index];
+}
+
+void HGraphBuilder::UpdateLocal(uint32_t register_index,
+ HInstruction* instruction,
+ uint32_t dex_pc) const {
+ HLocal* local = GetLocalAt(register_index);
+ current_block_->AddInstruction(new (arena_) HStoreLocal(local, instruction, dex_pc));
+}
+
+HInstruction* HGraphBuilder::LoadLocal(uint32_t register_index,
+ Primitive::Type type,
+ uint32_t dex_pc) const {
+ HLocal* local = GetLocalAt(register_index);
+ current_block_->AddInstruction(new (arena_) HLoadLocal(local, type, dex_pc));
+ return current_block_->GetLastInstruction();
}
} // namespace art
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 4f46d5e..50a1334 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -24,14 +24,14 @@
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
-#include "instruction_builder.h"
#include "optimizing_compiler_stats.h"
#include "primitive.h"
#include "nodes.h"
-#include "ssa_builder.h"
namespace art {
+class Instruction;
+
class HGraphBuilder : public ValueObject {
public:
HGraphBuilder(HGraph* graph,
@@ -42,65 +42,245 @@
CompilerDriver* driver,
OptimizingCompilerStats* compiler_stats,
const uint8_t* interpreter_metadata,
- Handle<mirror::DexCache> dex_cache,
- StackHandleScopeCollection* handles)
- : graph_(graph),
+ Handle<mirror::DexCache> dex_cache)
+ : arena_(graph->GetArena()),
+ locals_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
+ current_block_(nullptr),
+ graph_(graph),
dex_file_(dex_file),
code_item_(code_item),
dex_compilation_unit_(dex_compilation_unit),
compiler_driver_(driver),
- compilation_stats_(compiler_stats),
+ outer_compilation_unit_(outer_compilation_unit),
+ return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])),
+ code_start_(code_item.insns_),
block_builder_(graph, dex_file, code_item),
- ssa_builder_(graph, handles),
- instruction_builder_(graph,
- &block_builder_,
- &ssa_builder_,
- dex_file,
- code_item_,
- Primitive::GetType(dex_compilation_unit_->GetShorty()[0]),
- dex_compilation_unit,
- outer_compilation_unit,
- driver,
- interpreter_metadata,
- compiler_stats,
- dex_cache) {}
+ latest_result_(nullptr),
+ compilation_stats_(compiler_stats),
+ interpreter_metadata_(interpreter_metadata),
+ dex_cache_(dex_cache) {}
// Only for unit testing.
HGraphBuilder(HGraph* graph,
const DexFile::CodeItem& code_item,
- StackHandleScopeCollection* handles,
Primitive::Type return_type = Primitive::kPrimInt)
- : graph_(graph),
+ : arena_(graph->GetArena()),
+ locals_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
+ current_block_(nullptr),
+ graph_(graph),
dex_file_(nullptr),
code_item_(code_item),
dex_compilation_unit_(nullptr),
compiler_driver_(nullptr),
- null_dex_cache_(),
- compilation_stats_(nullptr),
+ outer_compilation_unit_(nullptr),
+ return_type_(return_type),
+ code_start_(code_item.insns_),
block_builder_(graph, nullptr, code_item),
- ssa_builder_(graph, handles),
- instruction_builder_(graph,
- &block_builder_,
- &ssa_builder_,
- /* dex_file */ nullptr,
- code_item_,
- return_type,
- /* dex_compilation_unit */ nullptr,
- /* outer_compilation_unit */ nullptr,
- /* compiler_driver */ nullptr,
- /* interpreter_metadata */ nullptr,
- /* compiler_stats */ nullptr,
- null_dex_cache_) {}
+ latest_result_(nullptr),
+ compilation_stats_(nullptr),
+ interpreter_metadata_(nullptr),
+ null_dex_cache_(),
+ dex_cache_(null_dex_cache_) {}
- GraphAnalysisResult BuildGraph();
+ GraphAnalysisResult BuildGraph(StackHandleScopeCollection* handles);
static constexpr const char* kBuilderPassName = "builder";
private:
- void MaybeRecordStat(MethodCompilationStat compilation_stat);
+ bool GenerateInstructions();
+ bool AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_pc);
+
+ void FindNativeDebugInfoLocations(ArenaBitVector* locations);
+
+ bool CanDecodeQuickenedInfo() const;
+ uint16_t LookupQuickenedInfo(uint32_t dex_pc);
+
+ HBasicBlock* FindBlockStartingAt(uint32_t dex_pc) const {
+ return block_builder_.GetBlockAt(dex_pc);
+ }
+
+ void InitializeLocals(uint16_t count);
+ HLocal* GetLocalAt(uint32_t register_index) const;
+ void UpdateLocal(uint32_t register_index, HInstruction* instruction, uint32_t dex_pc) const;
+ HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type, uint32_t dex_pc) const;
+ void InitializeParameters(uint16_t number_of_parameters);
+
+ // Returns whether the current method needs access check for the type.
+ // Output parameter finalizable is set to whether the type is finalizable.
+ bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const;
+
+ template<typename T>
+ void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_23x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_23x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ void Binop_23x_cmp(const Instruction& instruction,
+ Primitive::Type type,
+ ComparisonBias bias,
+ uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_12x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc);
+
+ template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_pc);
+ template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_pc);
+
+ void Conversion_12x(const Instruction& instruction,
+ Primitive::Type input_type,
+ Primitive::Type result_type,
+ uint32_t dex_pc);
+
+ void BuildCheckedDivRem(uint16_t out_reg,
+ uint16_t first_reg,
+ int64_t second_reg_or_constant,
+ uint32_t dex_pc,
+ Primitive::Type type,
+ bool second_is_lit,
+ bool is_div);
+
+ void BuildReturn(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ // Builds an instance field access node and returns whether the instruction is supported.
+ bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
+
+ void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type field_type);
+ // Builds a static field access node and returns whether the instruction is supported.
+ bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
+
+ void BuildArrayAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_get,
+ Primitive::Type anticipated_type);
+
+ // Builds an invocation node and returns whether the instruction is supported.
+ bool BuildInvoke(const Instruction& instruction,
+ uint32_t dex_pc,
+ uint32_t method_idx,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index);
+
+ // Builds a new array node and the instructions that fill it.
+ void BuildFilledNewArray(uint32_t dex_pc,
+ uint32_t type_index,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index);
+
+ void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc);
+
+ // Fills the given object with data as specified in the fill-array-data
+ // instruction. Currently only used for non-reference and non-floating point
+ // arrays.
+ template <typename T>
+ void BuildFillArrayData(HInstruction* object,
+ const T* data,
+ uint32_t element_count,
+ Primitive::Type anticipated_type,
+ uint32_t dex_pc);
+
+ // Fills the given object with data as specified in the fill-array-data
+ // instruction. The data must be for long and double arrays.
+ void BuildFillWideArrayData(HInstruction* object,
+ const int64_t* data,
+ uint32_t element_count,
+ uint32_t dex_pc);
+
+ // Builds a `HInstanceOf`, or a `HCheckCast` instruction.
+ void BuildTypeCheck(const Instruction& instruction,
+ uint8_t destination,
+ uint8_t reference,
+ uint16_t type_index,
+ uint32_t dex_pc);
+
+ // Builds an instruction sequence for a switch statement.
+ void BuildSwitch(const Instruction& instruction, uint32_t dex_pc);
+
bool SkipCompilation(size_t number_of_branches);
+ void MaybeRecordStat(MethodCompilationStat compilation_stat);
+
+ // Returns the outer-most compiling method's class.
+ mirror::Class* GetOutermostCompilingClass() const;
+
+ // Returns the class whose method is being compiled.
+ mirror::Class* GetCompilingClass() const;
+
+ // Returns whether `type_index` points to the outer-most compiling method's class.
+ bool IsOutermostCompilingClass(uint16_t type_index) const;
+
+ void PotentiallySimplifyFakeString(uint16_t original_dex_register,
+ uint32_t dex_pc,
+ HInvoke* invoke);
+
+ bool SetupInvokeArguments(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ size_t start_index,
+ size_t* argument_index);
+
+ bool HandleInvoke(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ HClinitCheck* clinit_check);
+
+ bool HandleStringInit(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor);
+
+ HClinitCheck* ProcessClinitCheckForInvoke(
+ uint32_t dex_pc,
+ ArtMethod* method,
+ uint32_t method_idx,
+ HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Build a HNewInstance instruction.
+ bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc);
+
+ // Return whether the compiler can assume `cls` is initialized.
+ bool IsInitialized(Handle<mirror::Class> cls) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Try to resolve a method using the class linker. Return null if a method could
+ // not be resolved.
+ ArtMethod* ResolveMethod(uint16_t method_idx, InvokeType invoke_type);
+
+ ArenaAllocator* const arena_;
+
+ ArenaVector<HLocal*> locals_;
+
+ HBasicBlock* current_block_;
HGraph* const graph_;
+
+ // The dex file where the method being compiled is, and the bytecode data.
const DexFile* const dex_file_;
const DexFile::CodeItem& code_item_;
@@ -110,13 +290,31 @@
CompilerDriver* const compiler_driver_;
- ScopedNullHandle<mirror::DexCache> null_dex_cache_;
+ // The compilation unit of the outermost method being compiled. That is the
+ // method being compiled (and not inlined), and potentially inlining other
+ // methods.
+ const DexCompilationUnit* const outer_compilation_unit_;
+
+ // The return type of the method being compiled.
+ const Primitive::Type return_type_;
+
+ // The pointer in the dex file where the instructions of the code item
+ // being currently compiled start.
+ const uint16_t* code_start_;
+
+ HBasicBlockBuilder block_builder_;
+
+ // The last invoke or fill-new-array being built. Only to be
+ // used by move-result instructions.
+ HInstruction* latest_result_;
OptimizingCompilerStats* compilation_stats_;
- HBasicBlockBuilder block_builder_;
- SsaBuilder ssa_builder_;
- HInstructionBuilder instruction_builder_;
+ const uint8_t* interpreter_metadata_;
+
+ // Dex cache for dex_file_.
+ ScopedNullHandle<mirror::DexCache> null_dex_cache_;
+ Handle<mirror::DexCache> dex_cache_;
DISALLOW_COPY_AND_ASSIGN(HGraphBuilder);
};
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 65e5c3a..1d2273d 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -299,6 +299,23 @@
}
}
+int32_t CodeGenerator::GetStackSlot(HLocal* local) const {
+ uint16_t reg_number = local->GetRegNumber();
+ uint16_t number_of_locals = GetGraph()->GetNumberOfLocalVRegs();
+ if (reg_number >= number_of_locals) {
+ // Local is a parameter of the method. It is stored in the caller's frame.
+ // TODO: Share this logic with StackVisitor::GetVRegOffsetFromQuickCode.
+ return GetFrameSize() + InstructionSetPointerSize(GetInstructionSet()) // ART method
+ + (reg_number - number_of_locals) * kVRegSize;
+ } else {
+ // Local is a temporary in this method. It is stored in this method's frame.
+ return GetFrameSize() - FrameEntrySpillSize()
+ - kVRegSize // filler.
+ - (number_of_locals * kVRegSize)
+ + (reg_number * kVRegSize);
+ }
+}
+
void CodeGenerator::CreateCommonInvokeLocationSummary(
HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor) {
ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 1a060b1..cad5529 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -211,6 +211,7 @@
size_t maximum_number_of_live_fpu_registers,
size_t number_of_out_slots,
const ArenaVector<HBasicBlock*>& block_order);
+ int32_t GetStackSlot(HLocal* local) const;
uint32_t GetFrameSize() const { return frame_size_; }
void SetFrameSize(uint32_t size) { frame_size_ = size; }
@@ -524,6 +525,8 @@
slow_paths_.reserve(8);
}
+ virtual Location GetStackLocation(HLoadLocal* load) const = 0;
+
virtual HGraphVisitor* GetLocationBuilder() = 0;
virtual HGraphVisitor* GetInstructionVisitor() = 0;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index a0c1412..98577d6 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -954,6 +954,30 @@
__ BindTrackedLabel(label);
}
+Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
+ switch (load->GetType()) {
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimFloat:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << load->GetType();
+ UNREACHABLE();
+ }
+
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+}
+
Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -1700,6 +1724,49 @@
HandleCondition(comp);
}
+void LocationsBuilderARM::VisitLocal(HLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) {
+ DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
+}
+
+void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) {
+ load->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
+ // Nothing to do, this is driven by the code generator.
+}
+
+void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
+ switch (store->InputAt(1)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimFloat:
+ locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType();
+ }
+}
+
+void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
+}
+
void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 144d58d..8434128 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -345,6 +345,8 @@
void SetupBlockedRegisters() const OVERRIDE;
+ Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 7699ddd..491014d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1072,6 +1072,31 @@
}
}
+Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
+ Primitive::Type type = load->GetType();
+
+ switch (type) {
+ case Primitive::kPrimNot:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << type;
+ }
+
+ LOG(FATAL) << "Unreachable";
+ return Location::NoLocation();
+}
+
void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register card = temps.AcquireX();
@@ -3985,6 +4010,14 @@
__ Str(wzr, GetExceptionTlsAddress());
}
+void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) {
+ load->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
+ // Nothing to do, this is driven by the code generator.
+}
+
HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) {
if (kEmitCompilerReadBarrier) {
@@ -4123,6 +4156,14 @@
}
}
+void LocationsBuilderARM64::VisitLocal(HLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitLocal(HLocal* local) {
+ DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
+}
+
void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
locations->SetOut(Location::ConstantLocation(constant));
@@ -4515,6 +4556,34 @@
HandleShift(shr);
}
+void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ Primitive::Type field_type = store->InputAt(1)->GetType();
+ switch (field_type) {
+ case Primitive::kPrimNot:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ default:
+ LOG(FATAL) << "Unimplemented local type " << field_type;
+ UNREACHABLE();
+ }
+}
+
+void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
+}
+
void LocationsBuilderARM64::VisitSub(HSub* instruction) {
HandleBinaryOp(instruction);
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index ec46a34..8ec7531 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -386,6 +386,8 @@
void SetupBlockedRegisters() const OVERRIDE;
+ Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 2df37cd..8b19f84 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -974,6 +974,31 @@
}
}
+Location CodeGeneratorMIPS::GetStackLocation(HLoadLocal* load) const {
+ Primitive::Type type = load->GetType();
+
+ switch (type) {
+ case Primitive::kPrimNot:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << type;
+ }
+
+ LOG(FATAL) << "Unreachable";
+ return Location::NoLocation();
+}
+
void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) {
MipsLabel done;
Register card = AT;
@@ -4038,6 +4063,14 @@
__ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
}
+void LocationsBuilderMIPS::VisitLoadLocal(HLoadLocal* load) {
+ load->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorMIPS::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
+ // Nothing to do, this is driven by the code generator.
+}
+
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
LocationSummary::CallKind call_kind = load->NeedsEnvironment()
? LocationSummary::kCallOnSlowPath
@@ -4063,6 +4096,14 @@
}
}
+void LocationsBuilderMIPS::VisitLocal(HLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorMIPS::VisitLocal(HLocal* local) {
+ DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
+}
+
void LocationsBuilderMIPS::VisitLongConstant(HLongConstant* constant) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
locations->SetOut(Location::ConstantLocation(constant));
@@ -4570,6 +4611,33 @@
HandleShift(shr);
}
+void LocationsBuilderMIPS::VisitStoreLocal(HStoreLocal* store) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ Primitive::Type field_type = store->InputAt(1)->GetType();
+ switch (field_type) {
+ case Primitive::kPrimNot:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ default:
+ LOG(FATAL) << "Unimplemented local type " << field_type;
+ }
+}
+
+void InstructionCodeGeneratorMIPS::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
+}
+
void LocationsBuilderMIPS::VisitSub(HSub* instruction) {
HandleBinaryOp(instruction);
}
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 5e6fec8..afe7917 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -290,6 +290,8 @@
void SetupBlockedRegisters() const OVERRIDE;
+ Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id);
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id);
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index cc1f372..2f9eca6 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -869,6 +869,31 @@
}
}
+Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const {
+ Primitive::Type type = load->GetType();
+
+ switch (type) {
+ case Primitive::kPrimNot:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << type;
+ }
+
+ LOG(FATAL) << "Unreachable";
+ return Location::NoLocation();
+}
+
void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object,
GpuRegister value,
bool value_can_be_null) {
@@ -3256,6 +3281,14 @@
__ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
}
+void LocationsBuilderMIPS64::VisitLoadLocal(HLoadLocal* load) {
+ load->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
+ // Nothing to do, this is driven by the code generator.
+}
+
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
LocationSummary::CallKind call_kind = load->NeedsEnvironment()
? LocationSummary::kCallOnSlowPath
@@ -3284,6 +3317,14 @@
}
}
+void LocationsBuilderMIPS64::VisitLocal(HLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitLocal(HLocal* local) {
+ DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
+}
+
void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
locations->SetOut(Location::ConstantLocation(constant));
@@ -3704,6 +3745,33 @@
HandleShift(shr);
}
+void LocationsBuilderMIPS64::VisitStoreLocal(HStoreLocal* store) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ Primitive::Type field_type = store->InputAt(1)->GetType();
+ switch (field_type) {
+ case Primitive::kPrimNot:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ default:
+ LOG(FATAL) << "Unimplemented local type " << field_type;
+ }
+}
+
+void InstructionCodeGeneratorMIPS64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
+}
+
void LocationsBuilderMIPS64::VisitSub(HSub* instruction) {
HandleBinaryOp(instruction);
}
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 4e15cdd..94767cb 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -286,6 +286,8 @@
void SetupBlockedRegisters() const OVERRIDE;
+ Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id);
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id);
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 94d2f0c..715b5be 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -892,6 +892,30 @@
__ Bind(GetLabelOf(block));
}
+Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const {
+ switch (load->GetType()) {
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimFloat:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << load->GetType();
+ UNREACHABLE();
+ }
+
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+}
+
Location InvokeDexCallingConventionVisitorX86::GetReturnLocation(Primitive::Type type) const {
switch (type) {
case Primitive::kPrimBoolean:
@@ -1622,6 +1646,49 @@
__ nop();
}
+void LocationsBuilderX86::VisitLocal(HLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86::VisitLocal(HLocal* local) {
+ DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
+}
+
+void LocationsBuilderX86::VisitLoadLocal(HLoadLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
+ // Nothing to do, this is driven by the code generator.
+}
+
+void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
+ switch (store->InputAt(1)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimFloat:
+ locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ default:
+ LOG(FATAL) << "Unknown local type " << store->InputAt(1)->GetType();
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
+}
+
void LocationsBuilderX86::HandleCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 69a6253..1fa22fc 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -367,6 +367,8 @@
void SetupBlockedRegisters() const OVERRIDE;
+ Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index da126e4..cc46a07 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1118,6 +1118,30 @@
__ Bind(GetLabelOf(block));
}
+Location CodeGeneratorX86_64::GetStackLocation(HLoadLocal* load) const {
+ switch (load->GetType()) {
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimFloat:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << load->GetType();
+ UNREACHABLE();
+ }
+
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+}
+
void CodeGeneratorX86_64::Move(Location destination, Location source) {
if (source.Equals(destination)) {
return;
@@ -1636,6 +1660,49 @@
__ nop();
}
+void LocationsBuilderX86_64::VisitLocal(HLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86_64::VisitLocal(HLocal* local) {
+ DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
+}
+
+void LocationsBuilderX86_64::VisitLoadLocal(HLoadLocal* local) {
+ local->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
+ // Nothing to do, this is driven by the code generator.
+}
+
+void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
+ switch (store->InputAt(1)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimFloat:
+ locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType();
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
+}
+
void LocationsBuilderX86_64::HandleCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index d7ce7c6..7ebce58 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -350,6 +350,8 @@
return GetLabelOf(block)->Position();
}
+ Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
+
void SetupBlockedRegisters() const OVERRIDE;
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index d1a2a26..b9081cb 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -111,21 +111,21 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant [3]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 4: IntConstant [7]\n"
+ " 2: SuspendCheck\n"
+ " 3: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 3: Neg(2) [4]\n"
- " 4: Return(3)\n"
+ " 7: Neg(4) [10]\n"
+ " 10: Return(7)\n"
"BasicBlock 2, pred: 1\n"
- " 5: Exit\n";
+ " 11: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: IntConstant [3]\n", " 2: IntConstant\n"
- " 6: IntConstant [4]\n" },
- { " 3: Neg(2) [4]\n", removed },
- { " 4: Return(3)\n", " 4: Return(6)\n" }
+ { " 4: IntConstant [7]\n", " 4: IntConstant\n"
+ " 12: IntConstant [10]\n" },
+ { " 7: Neg(4) [10]\n", removed },
+ { " 10: Return(7)\n", " 10: Return(12)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -138,7 +138,7 @@
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 2: IntConstant\n", removed },
+ { " 4: IntConstant\n", removed },
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -172,21 +172,21 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: LongConstant [3]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 6: LongConstant [9]\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 3: Neg(2) [4]\n"
- " 4: Return(3)\n"
+ " 9: Neg(6) [12]\n"
+ " 12: Return(9)\n"
"BasicBlock 2, pred: 1\n"
- " 5: Exit\n";
+ " 13: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: LongConstant [3]\n", " 2: LongConstant\n"
- " 6: LongConstant [4]\n" },
- { " 3: Neg(2) [4]\n", removed },
- { " 4: Return(3)\n", " 4: Return(6)\n" }
+ { " 6: LongConstant [9]\n", " 6: LongConstant\n"
+ " 14: LongConstant [12]\n" },
+ { " 9: Neg(6) [12]\n", removed },
+ { " 12: Return(9)\n", " 12: Return(14)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -199,7 +199,7 @@
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 2: LongConstant\n", removed },
+ { " 6: LongConstant\n", removed },
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -231,23 +231,23 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant [4]\n"
- " 3: IntConstant [4]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 5: IntConstant [11]\n"
+ " 7: IntConstant [11]\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 4: Add(2, 3) [5]\n"
- " 5: Return(4)\n"
+ " 11: Add(5, 7) [14]\n"
+ " 14: Return(11)\n"
"BasicBlock 2, pred: 1\n"
- " 6: Exit\n";
+ " 15: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
- { " 3: IntConstant [4]\n", " 3: IntConstant\n"
- " 7: IntConstant [5]\n" },
- { " 4: Add(2, 3) [5]\n", removed },
- { " 5: Return(4)\n", " 5: Return(7)\n" }
+ { " 5: IntConstant [11]\n", " 5: IntConstant\n" },
+ { " 7: IntConstant [11]\n", " 7: IntConstant\n"
+ " 16: IntConstant [14]\n" },
+ { " 11: Add(5, 7) [14]\n", removed },
+ { " 14: Return(11)\n", " 14: Return(16)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -260,8 +260,8 @@
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 2: IntConstant\n", removed },
- { " 3: IntConstant\n", removed }
+ { " 5: IntConstant\n", removed },
+ { " 7: IntConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -300,33 +300,33 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant [4]\n"
- " 3: IntConstant [4]\n"
- " 5: IntConstant [7]\n"
- " 6: IntConstant [7]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 5: IntConstant [11]\n"
+ " 7: IntConstant [11]\n"
+ " 13: IntConstant [19]\n"
+ " 15: IntConstant [19]\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 4: Add(2, 3) [8]\n"
- " 7: Add(5, 6) [8]\n"
- " 8: Add(4, 7) [9]\n"
- " 9: Return(8)\n"
+ " 11: Add(5, 7) [23]\n"
+ " 19: Add(13, 15) [23]\n"
+ " 23: Add(11, 19) [26]\n"
+ " 26: Return(23)\n"
"BasicBlock 2, pred: 1\n"
- " 10: Exit\n";
+ " 27: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
- { " 3: IntConstant [4]\n", " 3: IntConstant\n" },
- { " 5: IntConstant [7]\n", " 5: IntConstant\n" },
- { " 6: IntConstant [7]\n", " 6: IntConstant\n"
- " 11: IntConstant\n"
- " 12: IntConstant\n"
- " 13: IntConstant [9]\n" },
- { " 4: Add(2, 3) [8]\n", removed },
- { " 7: Add(5, 6) [8]\n", removed },
- { " 8: Add(4, 7) [9]\n", removed },
- { " 9: Return(8)\n", " 9: Return(13)\n" }
+ { " 5: IntConstant [11]\n", " 5: IntConstant\n" },
+ { " 7: IntConstant [11]\n", " 7: IntConstant\n" },
+ { " 13: IntConstant [19]\n", " 13: IntConstant\n" },
+ { " 15: IntConstant [19]\n", " 15: IntConstant\n"
+ " 28: IntConstant\n"
+ " 29: IntConstant\n"
+ " 30: IntConstant [26]\n" },
+ { " 11: Add(5, 7) [23]\n", removed },
+ { " 19: Add(13, 15) [23]\n", removed },
+ { " 23: Add(11, 19) [26]\n", removed },
+ { " 26: Return(23)\n", " 26: Return(30)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -345,12 +345,12 @@
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 2: IntConstant\n", removed },
- { " 3: IntConstant\n", removed },
{ " 5: IntConstant\n", removed },
- { " 6: IntConstant\n", removed },
- { " 11: IntConstant\n", removed },
- { " 12: IntConstant\n", removed }
+ { " 7: IntConstant\n", removed },
+ { " 13: IntConstant\n", removed },
+ { " 15: IntConstant\n", removed },
+ { " 28: IntConstant\n", removed },
+ { " 29: IntConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -381,23 +381,23 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant [4]\n"
- " 3: IntConstant [4]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 5: IntConstant [11]\n"
+ " 7: IntConstant [11]\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 4: Sub(2, 3) [5]\n"
- " 5: Return(4)\n"
+ " 11: Sub(5, 7) [14]\n"
+ " 14: Return(11)\n"
"BasicBlock 2, pred: 1\n"
- " 6: Exit\n";
+ " 15: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
- { " 3: IntConstant [4]\n", " 3: IntConstant\n"
- " 7: IntConstant [5]\n" },
- { " 4: Sub(2, 3) [5]\n", removed },
- { " 5: Return(4)\n", " 5: Return(7)\n" }
+ { " 5: IntConstant [11]\n", " 5: IntConstant\n" },
+ { " 7: IntConstant [11]\n", " 7: IntConstant\n"
+ " 16: IntConstant [14]\n" },
+ { " 11: Sub(5, 7) [14]\n", removed },
+ { " 14: Return(11)\n", " 14: Return(16)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -410,8 +410,8 @@
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 2: IntConstant\n", removed },
- { " 3: IntConstant\n", removed }
+ { " 5: IntConstant\n", removed },
+ { " 7: IntConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -444,23 +444,23 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: LongConstant [4]\n"
- " 3: LongConstant [4]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 8: LongConstant [14]\n"
+ " 10: LongConstant [14]\n"
+ " 6: SuspendCheck\n"
+ " 7: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 4: Add(2, 3) [5]\n"
- " 5: Return(4)\n"
+ " 14: Add(8, 10) [17]\n"
+ " 17: Return(14)\n"
"BasicBlock 2, pred: 1\n"
- " 6: Exit\n";
+ " 18: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: LongConstant [4]\n", " 2: LongConstant\n" },
- { " 3: LongConstant [4]\n", " 3: LongConstant\n"
- " 7: LongConstant [5]\n" },
- { " 4: Add(2, 3) [5]\n", removed },
- { " 5: Return(4)\n", " 5: Return(7)\n" }
+ { " 8: LongConstant [14]\n", " 8: LongConstant\n" },
+ { " 10: LongConstant [14]\n", " 10: LongConstant\n"
+ " 19: LongConstant [17]\n" },
+ { " 14: Add(8, 10) [17]\n", removed },
+ { " 17: Return(14)\n", " 17: Return(19)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -473,8 +473,8 @@
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 2: LongConstant\n", removed },
- { " 3: LongConstant\n", removed }
+ { " 8: LongConstant\n", removed },
+ { " 10: LongConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -508,23 +508,23 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: LongConstant [4]\n"
- " 3: LongConstant [4]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 8: LongConstant [14]\n"
+ " 10: LongConstant [14]\n"
+ " 6: SuspendCheck\n"
+ " 7: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 4: Sub(2, 3) [5]\n"
- " 5: Return(4)\n"
+ " 14: Sub(8, 10) [17]\n"
+ " 17: Return(14)\n"
"BasicBlock 2, pred: 1\n"
- " 6: Exit\n";
+ " 18: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: LongConstant [4]\n", " 2: LongConstant\n" },
- { " 3: LongConstant [4]\n", " 3: LongConstant\n"
- " 7: LongConstant [5]\n" },
- { " 4: Sub(2, 3) [5]\n", removed },
- { " 5: Return(4)\n", " 5: Return(7)\n" }
+ { " 8: LongConstant [14]\n", " 8: LongConstant\n" },
+ { " 10: LongConstant [14]\n", " 10: LongConstant\n"
+ " 19: LongConstant [17]\n" },
+ { " 14: Sub(8, 10) [17]\n", removed },
+ { " 17: Return(14)\n", " 17: Return(19)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -537,8 +537,8 @@
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 2: LongConstant\n", removed },
- { " 3: LongConstant\n", removed }
+ { " 8: LongConstant\n", removed },
+ { " 10: LongConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -587,44 +587,44 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant [4]\n" // v0 <- 1
- " 3: IntConstant [4]\n" // v1 <- 2
- " 6: IntConstant [7]\n" // const 5
- " 9: IntConstant [10]\n" // const 4
- " 12: IntConstant [13]\n" // const 8
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 5: IntConstant [11]\n" // v0 <- 1
+ " 7: IntConstant [11]\n" // v1 <- 2
+ " 15: IntConstant [16]\n" // const 5
+ " 20: IntConstant [21]\n" // const 4
+ " 25: IntConstant [26]\n" // const 8
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
- " 4: Add(2, 3) [7]\n" // v2 <- v0 + v1 = 1 + 2 = 3
- " 5: Goto 3\n" // goto L2
+ " 11: Add(5, 7) [21]\n" // v2 <- v0 + v1 = 1 + 2 = 3
+ " 13: Goto 3\n" // goto L2
"BasicBlock 2, pred: 3, succ: 4\n" // L1:
- " 10: Add(7, 9) [13]\n" // v1 <- v0 + 3 = 7 + 5 = 12
- " 11: Goto 4\n" // goto L3
+ " 16: Add(21, 15) [26]\n" // v1 <- v0 + 3 = 7 + 5 = 12
+ " 18: Goto 4\n" // goto L3
"BasicBlock 3, pred: 1, succ: 2\n" // L2:
- " 7: Add(4, 6) [10]\n" // v0 <- v2 + 2 = 3 + 4 = 7
- " 8: Goto 2\n" // goto L1
+ " 21: Add(11, 20) [16]\n" // v0 <- v2 + 2 = 3 + 4 = 7
+ " 23: Goto 2\n" // goto L1
"BasicBlock 4, pred: 2, succ: 5\n" // L3:
- " 13: Add(10, 12) [14]\n" // v2 <- v1 + 4 = 12 + 8 = 20
- " 14: Return(13)\n" // return v2
+ " 26: Add(16, 25) [29]\n" // v2 <- v1 + 4 = 12 + 8 = 20
+ " 29: Return(26)\n" // return v2
"BasicBlock 5, pred: 4\n"
- " 15: Exit\n";
+ " 30: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
- { " 3: IntConstant [4]\n", " 3: IntConstant\n" },
- { " 6: IntConstant [7]\n", " 6: IntConstant\n" },
- { " 9: IntConstant [10]\n", " 9: IntConstant\n" },
- { " 12: IntConstant [13]\n", " 12: IntConstant\n"
- " 16: IntConstant\n"
- " 17: IntConstant\n"
- " 18: IntConstant\n"
- " 19: IntConstant [14]\n" },
- { " 4: Add(2, 3) [7]\n", removed },
- { " 10: Add(7, 9) [13]\n", removed },
- { " 7: Add(4, 6) [10]\n", removed },
- { " 13: Add(10, 12) [14]\n", removed },
- { " 14: Return(13)\n", " 14: Return(19)\n"}
+ { " 5: IntConstant [11]\n", " 5: IntConstant\n" },
+ { " 7: IntConstant [11]\n", " 7: IntConstant\n" },
+ { " 15: IntConstant [16]\n", " 15: IntConstant\n" },
+ { " 20: IntConstant [21]\n", " 20: IntConstant\n" },
+ { " 25: IntConstant [26]\n", " 25: IntConstant\n"
+ " 31: IntConstant\n"
+ " 32: IntConstant\n"
+ " 33: IntConstant\n"
+ " 34: IntConstant [29]\n" },
+ { " 11: Add(5, 7) [21]\n", removed },
+ { " 16: Add(21, 15) [26]\n", removed },
+ { " 21: Add(11, 20) [16]\n", removed },
+ { " 26: Add(16, 25) [29]\n", removed },
+ { " 29: Return(26)\n", " 29: Return(34)\n"}
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -647,13 +647,13 @@
// Expected difference after dead code elimination.
std::string expected_after_dce =
"BasicBlock 0, succ: 1\n"
- " 19: IntConstant [14]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 34: IntConstant [29]\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5\n"
- " 14: Return(19)\n"
+ " 29: Return(34)\n"
"BasicBlock 5, pred: 1\n"
- " 15: Exit\n";
+ " 30: Exit\n";
TestCode(data,
expected_before,
@@ -685,31 +685,31 @@
Instruction::RETURN_VOID);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9, 8, 5]\n"
- " 4: IntConstant [8, 5]\n"
- " 1: SuspendCheck\n"
- " 2: Goto 1\n"
+ "BasicBlock 0, succ: 1\n"
+ " 6: IntConstant [18, 22, 11]\n"
+ " 8: IntConstant [22, 11]\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5, 2\n"
- " 5: GreaterThanOrEqual(3, 4) [6]\n"
- " 6: If(5)\n"
+ " 11: GreaterThanOrEqual(6, 8) [12]\n"
+ " 12: If(11)\n"
"BasicBlock 2, pred: 1, succ: 3\n"
- " 7: Goto 3\n"
+ " 15: Goto 3\n"
"BasicBlock 3, pred: 5, 2, succ: 4\n"
- " 8: Phi(4, 3) [9]\n"
- " 9: Add(8, 3)\n"
- " 10: ReturnVoid\n"
+ " 22: Phi(8, 6) [18]\n"
+ " 18: Add(22, 6)\n"
+ " 20: ReturnVoid\n"
"BasicBlock 4, pred: 3\n"
- " 11: Exit\n"
+ " 21: Exit\n"
"BasicBlock 5, pred: 1, succ: 3\n"
" 0: Goto 3\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 3: IntConstant [9, 8, 5]\n", " 3: IntConstant [6, 9, 8]\n" },
- { " 4: IntConstant [8, 5]\n", " 4: IntConstant [8]\n" },
- { " 5: GreaterThanOrEqual(3, 4) [6]\n", removed },
- { " 6: If(5)\n", " 6: If(3)\n" }
+ { " 6: IntConstant [18, 22, 11]\n", " 6: IntConstant [12, 18, 22]\n" },
+ { " 8: IntConstant [22, 11]\n", " 8: IntConstant [22]\n" },
+ { " 11: GreaterThanOrEqual(6, 8) [12]\n", removed },
+ { " 12: If(11)\n", " 12: If(6)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -723,12 +723,12 @@
// Expected graph after dead code elimination.
std::string expected_after_dce =
"BasicBlock 0, succ: 1\n"
- " 1: SuspendCheck\n"
- " 2: Goto 1\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 4\n"
- " 10: ReturnVoid\n"
+ " 20: ReturnVoid\n"
"BasicBlock 4, pred: 1\n"
- " 11: Exit\n";
+ " 21: Exit\n";
TestCode(data,
expected_before,
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index fe52aac..04bbd9c 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -79,29 +79,29 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9, 8, 5]\n"
- " 4: IntConstant [8, 5]\n"
- " 1: SuspendCheck\n"
- " 2: Goto 1\n"
+ " 6: IntConstant [18, 22, 11]\n"
+ " 8: IntConstant [22, 11]\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5, 2\n"
- " 5: GreaterThanOrEqual(3, 4) [6]\n"
- " 6: If(5)\n"
+ " 11: GreaterThanOrEqual(6, 8) [12]\n"
+ " 12: If(11)\n"
"BasicBlock 2, pred: 1, succ: 3\n"
- " 7: Goto 3\n"
+ " 15: Goto 3\n"
"BasicBlock 3, pred: 5, 2, succ: 4\n"
- " 8: Phi(4, 3) [9]\n"
- " 9: Add(8, 3)\n"
- " 10: ReturnVoid\n"
+ " 22: Phi(8, 6) [18]\n"
+ " 18: Add(22, 6)\n"
+ " 20: ReturnVoid\n"
"BasicBlock 4, pred: 3\n"
- " 11: Exit\n"
+ " 21: Exit\n"
"BasicBlock 5, pred: 1, succ: 3\n"
" 0: Goto 3\n";
// Expected difference after dead code elimination.
diff_t expected_diff = {
- { " 3: IntConstant [9, 8, 5]\n", " 3: IntConstant [8, 5]\n" },
- { " 8: Phi(4, 3) [9]\n", " 8: Phi(4, 3)\n" },
- { " 9: Add(8, 3)\n", removed }
+ { " 6: IntConstant [18, 22, 11]\n", " 6: IntConstant [22, 11]\n" },
+ { " 22: Phi(8, 6) [18]\n", " 22: Phi(8, 6)\n" },
+ { " 18: Add(22, 6)\n", removed }
};
std::string expected_after = Patch(expected_before, expected_diff);
@@ -145,36 +145,36 @@
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant [4]\n"
- " 3: IntConstant [4]\n"
- " 6: IntConstant [7]\n"
- " 9: IntConstant [10]\n"
- " 12: IntConstant [13]\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 5: IntConstant [11]\n"
+ " 7: IntConstant [11]\n"
+ " 15: IntConstant [16]\n"
+ " 20: IntConstant [21]\n"
+ " 25: IntConstant [26]\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
- " 4: Add(2, 3) [7]\n"
- " 5: Goto 3\n"
+ " 11: Add(5, 7) [21]\n"
+ " 13: Goto 3\n"
"BasicBlock 2, pred: 3, succ: 4\n"
- " 10: Add(7, 9) [13]\n"
- " 11: Goto 4\n"
+ " 16: Add(21, 15) [26]\n"
+ " 18: Goto 4\n"
"BasicBlock 3, pred: 1, succ: 2\n"
- " 7: Add(4, 6) [10]\n"
- " 8: Goto 2\n"
+ " 21: Add(11, 20) [16]\n"
+ " 23: Goto 2\n"
"BasicBlock 4, pred: 2, succ: 5\n"
- " 13: Add(10, 12)\n"
- " 14: ReturnVoid\n"
+ " 26: Add(16, 25)\n"
+ " 28: ReturnVoid\n"
"BasicBlock 5, pred: 4\n"
- " 15: Exit\n";
+ " 29: Exit\n";
std::string expected_after =
"BasicBlock 0, succ: 1\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5\n"
- " 14: ReturnVoid\n"
+ " 28: ReturnVoid\n"
"BasicBlock 5, pred: 1\n"
- " 15: Exit\n";
+ " 29: Exit\n";
TestCode(data, expected_before, expected_after);
}
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 6abf00e..56dc088 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -357,10 +357,8 @@
Primitive::kPrimBoolean);
entry->AddInstruction(parameter);
entry->AddInstruction(new (&allocator) HGoto());
- outer_loop_header->AddInstruction(new (&allocator) HSuspendCheck());
outer_loop_header->AddInstruction(new (&allocator) HIf(parameter));
outer_loop_body->AddInstruction(new (&allocator) HGoto());
- inner_loop_header->AddInstruction(new (&allocator) HSuspendCheck());
inner_loop_header->AddInstruction(new (&allocator) HIf(parameter));
inner_loop_body->AddInstruction(new (&allocator) HGoto());
inner_loop_exit->AddInstruction(new (&allocator) HGoto());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 7068e8b..e3172df 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1070,10 +1070,9 @@
compiler_driver_,
&inline_stats,
resolved_method->GetQuickenedInfo(),
- dex_cache,
- handles_);
+ dex_cache);
- if (builder.BuildGraph() != kAnalysisSuccess) {
+ if (builder.BuildGraph(handles_) != kAnalysisSuccess) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be built, so cannot be inlined";
return false;
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
deleted file mode 100644
index c5f2342..0000000
--- a/compiler/optimizing/instruction_builder.cc
+++ /dev/null
@@ -1,2681 +0,0 @@
-/*
- * Copyright (C) 2016 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 "instruction_builder.h"
-
-#include "bytecode_utils.h"
-#include "class_linker.h"
-#include "driver/compiler_options.h"
-#include "scoped_thread_state_change.h"
-
-namespace art {
-
-void HInstructionBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) {
- if (compilation_stats_ != nullptr) {
- compilation_stats_->RecordStat(compilation_stat);
- }
-}
-
-HBasicBlock* HInstructionBuilder::FindBlockStartingAt(uint32_t dex_pc) const {
- return block_builder_->GetBlockAt(dex_pc);
-}
-
-ArenaVector<HInstruction*>* HInstructionBuilder::GetLocalsFor(HBasicBlock* block) {
- ArenaVector<HInstruction*>* locals = &locals_for_[block->GetBlockId()];
- const size_t vregs = graph_->GetNumberOfVRegs();
- if (locals->size() != vregs) {
- locals->resize(vregs, nullptr);
-
- if (block->IsCatchBlock()) {
- // We record incoming inputs of catch phis at throwing instructions and
- // must therefore eagerly create the phis. Phis for undefined vregs will
- // be deleted when the first throwing instruction with the vreg undefined
- // is encountered. Unused phis will be removed by dead phi analysis.
- for (size_t i = 0; i < vregs; ++i) {
- // No point in creating the catch phi if it is already undefined at
- // the first throwing instruction.
- HInstruction* current_local_value = (*current_locals_)[i];
- if (current_local_value != nullptr) {
- HPhi* phi = new (arena_) HPhi(
- arena_,
- i,
- 0,
- current_local_value->GetType());
- block->AddPhi(phi);
- (*locals)[i] = phi;
- }
- }
- }
- }
- return locals;
-}
-
-HInstruction* HInstructionBuilder::ValueOfLocalAt(HBasicBlock* block, size_t local) {
- ArenaVector<HInstruction*>* locals = GetLocalsFor(block);
- return (*locals)[local];
-}
-
-void HInstructionBuilder::InitializeBlockLocals() {
- current_locals_ = GetLocalsFor(current_block_);
-
- if (current_block_->IsCatchBlock()) {
- // Catch phis were already created and inputs collected from throwing sites.
- if (kIsDebugBuild) {
- // Make sure there was at least one throwing instruction which initialized
- // locals (guaranteed by HGraphBuilder) and that all try blocks have been
- // visited already (from HTryBoundary scoping and reverse post order).
- bool catch_block_visited = false;
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* current = it.Current();
- if (current == current_block_) {
- catch_block_visited = true;
- } else if (current->IsTryBlock()) {
- const HTryBoundary& try_entry = current->GetTryCatchInformation()->GetTryEntry();
- if (try_entry.HasExceptionHandler(*current_block_)) {
- DCHECK(!catch_block_visited) << "Catch block visited before its try block.";
- }
- }
- }
- DCHECK_EQ(current_locals_->size(), graph_->GetNumberOfVRegs())
- << "No instructions throwing into a live catch block.";
- }
- } else if (current_block_->IsLoopHeader()) {
- // If the block is a loop header, we know we only have visited the pre header
- // because we are visiting in reverse post order. We create phis for all initialized
- // locals from the pre header. Their inputs will be populated at the end of
- // the analysis.
- for (size_t local = 0; local < current_locals_->size(); ++local) {
- HInstruction* incoming =
- ValueOfLocalAt(current_block_->GetLoopInformation()->GetPreHeader(), local);
- if (incoming != nullptr) {
- HPhi* phi = new (arena_) HPhi(
- arena_,
- local,
- 0,
- incoming->GetType());
- current_block_->AddPhi(phi);
- (*current_locals_)[local] = phi;
- }
- }
-
- // Save the loop header so that the last phase of the analysis knows which
- // blocks need to be updated.
- loop_headers_.push_back(current_block_);
- } else if (current_block_->GetPredecessors().size() > 0) {
- // All predecessors have already been visited because we are visiting in reverse post order.
- // We merge the values of all locals, creating phis if those values differ.
- for (size_t local = 0; local < current_locals_->size(); ++local) {
- bool one_predecessor_has_no_value = false;
- bool is_different = false;
- HInstruction* value = ValueOfLocalAt(current_block_->GetPredecessors()[0], local);
-
- for (HBasicBlock* predecessor : current_block_->GetPredecessors()) {
- HInstruction* current = ValueOfLocalAt(predecessor, local);
- if (current == nullptr) {
- one_predecessor_has_no_value = true;
- break;
- } else if (current != value) {
- is_different = true;
- }
- }
-
- if (one_predecessor_has_no_value) {
- // If one predecessor has no value for this local, we trust the verifier has
- // successfully checked that there is a store dominating any read after this block.
- continue;
- }
-
- if (is_different) {
- HInstruction* first_input = ValueOfLocalAt(current_block_->GetPredecessors()[0], local);
- HPhi* phi = new (arena_) HPhi(
- arena_,
- local,
- current_block_->GetPredecessors().size(),
- first_input->GetType());
- for (size_t i = 0; i < current_block_->GetPredecessors().size(); i++) {
- HInstruction* pred_value = ValueOfLocalAt(current_block_->GetPredecessors()[i], local);
- phi->SetRawInputAt(i, pred_value);
- }
- current_block_->AddPhi(phi);
- value = phi;
- }
- (*current_locals_)[local] = value;
- }
- }
-}
-
-void HInstructionBuilder::PropagateLocalsToCatchBlocks() {
- const HTryBoundary& try_entry = current_block_->GetTryCatchInformation()->GetTryEntry();
- for (HBasicBlock* catch_block : try_entry.GetExceptionHandlers()) {
- ArenaVector<HInstruction*>* handler_locals = GetLocalsFor(catch_block);
- DCHECK_EQ(handler_locals->size(), current_locals_->size());
- for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
- HInstruction* handler_value = (*handler_locals)[vreg];
- if (handler_value == nullptr) {
- // Vreg was undefined at a previously encountered throwing instruction
- // and the catch phi was deleted. Do not record the local value.
- continue;
- }
- DCHECK(handler_value->IsPhi());
-
- HInstruction* local_value = (*current_locals_)[vreg];
- if (local_value == nullptr) {
- // This is the first instruction throwing into `catch_block` where
- // `vreg` is undefined. Delete the catch phi.
- catch_block->RemovePhi(handler_value->AsPhi());
- (*handler_locals)[vreg] = nullptr;
- } else {
- // Vreg has been defined at all instructions throwing into `catch_block`
- // encountered so far. Record the local value in the catch phi.
- handler_value->AsPhi()->AddInput(local_value);
- }
- }
- }
-}
-
-void HInstructionBuilder::AppendInstruction(HInstruction* instruction) {
- current_block_->AddInstruction(instruction);
- InitializeInstruction(instruction);
-}
-
-void HInstructionBuilder::InsertInstructionAtTop(HInstruction* instruction) {
- if (current_block_->GetInstructions().IsEmpty()) {
- current_block_->AddInstruction(instruction);
- } else {
- current_block_->InsertInstructionBefore(instruction, current_block_->GetFirstInstruction());
- }
- InitializeInstruction(instruction);
-}
-
-void HInstructionBuilder::InitializeInstruction(HInstruction* instruction) {
- if (instruction->NeedsEnvironment()) {
- HEnvironment* environment = new (arena_) HEnvironment(
- arena_,
- current_locals_->size(),
- graph_->GetDexFile(),
- graph_->GetMethodIdx(),
- instruction->GetDexPc(),
- graph_->GetInvokeType(),
- instruction);
- environment->CopyFrom(*current_locals_);
- instruction->SetRawEnvironment(environment);
- }
-}
-
-void HInstructionBuilder::SetLoopHeaderPhiInputs() {
- for (size_t i = loop_headers_.size(); i > 0; --i) {
- HBasicBlock* block = loop_headers_[i - 1];
- for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
- HPhi* phi = it.Current()->AsPhi();
- size_t vreg = phi->GetRegNumber();
- for (HBasicBlock* predecessor : block->GetPredecessors()) {
- HInstruction* value = ValueOfLocalAt(predecessor, vreg);
- if (value == nullptr) {
- // Vreg is undefined at this predecessor. Mark it dead and leave with
- // fewer inputs than predecessors. SsaChecker will fail if not removed.
- phi->SetDead();
- break;
- } else {
- phi->AddInput(value);
- }
- }
- }
- }
-}
-
-static bool IsBlockPopulated(HBasicBlock* block) {
- if (block->IsLoopHeader()) {
- // Suspend checks were inserted into loop headers during building of dominator tree.
- DCHECK(block->GetFirstInstruction()->IsSuspendCheck());
- return block->GetFirstInstruction() != block->GetLastInstruction();
- } else {
- return !block->GetInstructions().IsEmpty();
- }
-}
-
-bool HInstructionBuilder::Build() {
- locals_for_.resize(graph_->GetBlocks().size(),
- ArenaVector<HInstruction*>(arena_->Adapter(kArenaAllocGraphBuilder)));
-
- // Find locations where we want to generate extra stackmaps for native debugging.
- // This allows us to generate the info only at interesting points (for example,
- // at start of java statement) rather than before every dex instruction.
- const bool native_debuggable = compiler_driver_ != nullptr &&
- compiler_driver_->GetCompilerOptions().GetNativeDebuggable();
- ArenaBitVector* native_debug_info_locations = nullptr;
- if (native_debuggable) {
- const uint32_t num_instructions = code_item_.insns_size_in_code_units_;
- native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false);
- FindNativeDebugInfoLocations(native_debug_info_locations);
- }
-
- for (HReversePostOrderIterator block_it(*graph_); !block_it.Done(); block_it.Advance()) {
- current_block_ = block_it.Current();
- uint32_t block_dex_pc = current_block_->GetDexPc();
-
- InitializeBlockLocals();
-
- if (current_block_->IsEntryBlock()) {
- InitializeParameters();
- AppendInstruction(new (arena_) HSuspendCheck(0u));
- AppendInstruction(new (arena_) HGoto(0u));
- continue;
- } else if (current_block_->IsExitBlock()) {
- AppendInstruction(new (arena_) HExit());
- continue;
- } else if (current_block_->IsLoopHeader()) {
- HSuspendCheck* suspend_check = new (arena_) HSuspendCheck(current_block_->GetDexPc());
- current_block_->GetLoopInformation()->SetSuspendCheck(suspend_check);
- // This is slightly odd because the loop header might not be empty (TryBoundary).
- // But we're still creating the environment with locals from the top of the block.
- InsertInstructionAtTop(suspend_check);
- }
-
- if (block_dex_pc == kNoDexPc || current_block_ != block_builder_->GetBlockAt(block_dex_pc)) {
- // Synthetic block that does not need to be populated.
- DCHECK(IsBlockPopulated(current_block_));
- continue;
- }
-
- DCHECK(!IsBlockPopulated(current_block_));
-
- for (CodeItemIterator it(code_item_, block_dex_pc); !it.Done(); it.Advance()) {
- if (current_block_ == nullptr) {
- // The previous instruction ended this block.
- break;
- }
-
- uint32_t dex_pc = it.CurrentDexPc();
- if (dex_pc != block_dex_pc && FindBlockStartingAt(dex_pc) != nullptr) {
- // This dex_pc starts a new basic block.
- break;
- }
-
- if (current_block_->IsTryBlock() && IsThrowingDexInstruction(it.CurrentInstruction())) {
- PropagateLocalsToCatchBlocks();
- }
-
- if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
- AppendInstruction(new (arena_) HNativeDebugInfo(dex_pc));
- }
-
- if (!ProcessDexInstruction(it.CurrentInstruction(), dex_pc)) {
- return false;
- }
- }
-
- if (current_block_ != nullptr) {
- // Branching instructions clear current_block, so we know the last
- // instruction of the current block is not a branching instruction.
- // We add an unconditional Goto to the next block.
- DCHECK_EQ(current_block_->GetSuccessors().size(), 1u);
- AppendInstruction(new (arena_) HGoto());
- }
- }
-
- SetLoopHeaderPhiInputs();
-
- return true;
-}
-
-void HInstructionBuilder::FindNativeDebugInfoLocations(ArenaBitVector* locations) {
- // The callback gets called when the line number changes.
- // In other words, it marks the start of new java statement.
- struct Callback {
- static bool Position(void* ctx, const DexFile::PositionInfo& entry) {
- static_cast<ArenaBitVector*>(ctx)->SetBit(entry.address_);
- return false;
- }
- };
- dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations);
- // Instruction-specific tweaks.
- const Instruction* const begin = Instruction::At(code_item_.insns_);
- const Instruction* const end = begin->RelativeAt(code_item_.insns_size_in_code_units_);
- for (const Instruction* inst = begin; inst < end; inst = inst->Next()) {
- switch (inst->Opcode()) {
- case Instruction::MOVE_EXCEPTION: {
- // Stop in native debugger after the exception has been moved.
- // The compiler also expects the move at the start of basic block so
- // we do not want to interfere by inserting native-debug-info before it.
- locations->ClearBit(inst->GetDexPc(code_item_.insns_));
- const Instruction* next = inst->Next();
- if (next < end) {
- locations->SetBit(next->GetDexPc(code_item_.insns_));
- }
- break;
- }
- default:
- break;
- }
- }
-}
-
-HInstruction* HInstructionBuilder::LoadLocal(uint32_t reg_number, Primitive::Type type) const {
- HInstruction* value = (*current_locals_)[reg_number];
- DCHECK(value != nullptr);
-
- // If the operation requests a specific type, we make sure its input is of that type.
- if (type != value->GetType()) {
- if (Primitive::IsFloatingPointType(type)) {
- return ssa_builder_->GetFloatOrDoubleEquivalent(value, type);
- } else if (type == Primitive::kPrimNot) {
- return ssa_builder_->GetReferenceTypeEquivalent(value);
- }
- }
-
- return value;
-}
-
-void HInstructionBuilder::UpdateLocal(uint32_t reg_number, HInstruction* stored_value) {
- Primitive::Type stored_type = stored_value->GetType();
- DCHECK_NE(stored_type, Primitive::kPrimVoid);
-
- // Storing into vreg `reg_number` may implicitly invalidate the surrounding
- // registers. Consider the following cases:
- // (1) Storing a wide value must overwrite previous values in both `reg_number`
- // and `reg_number+1`. We store `nullptr` in `reg_number+1`.
- // (2) If vreg `reg_number-1` holds a wide value, writing into `reg_number`
- // must invalidate it. We store `nullptr` in `reg_number-1`.
- // Consequently, storing a wide value into the high vreg of another wide value
- // will invalidate both `reg_number-1` and `reg_number+1`.
-
- if (reg_number != 0) {
- HInstruction* local_low = (*current_locals_)[reg_number - 1];
- if (local_low != nullptr && Primitive::Is64BitType(local_low->GetType())) {
- // The vreg we are storing into was previously the high vreg of a pair.
- // We need to invalidate its low vreg.
- DCHECK((*current_locals_)[reg_number] == nullptr);
- (*current_locals_)[reg_number - 1] = nullptr;
- }
- }
-
- (*current_locals_)[reg_number] = stored_value;
- if (Primitive::Is64BitType(stored_type)) {
- // We are storing a pair. Invalidate the instruction in the high vreg.
- (*current_locals_)[reg_number + 1] = nullptr;
- }
-}
-
-void HInstructionBuilder::InitializeParameters() {
- DCHECK(current_block_->IsEntryBlock());
-
- // dex_compilation_unit_ is null only when unit testing.
- if (dex_compilation_unit_ == nullptr) {
- return;
- }
-
- const char* shorty = dex_compilation_unit_->GetShorty();
- uint16_t number_of_parameters = graph_->GetNumberOfInVRegs();
- uint16_t locals_index = graph_->GetNumberOfLocalVRegs();
- uint16_t parameter_index = 0;
-
- const DexFile::MethodId& referrer_method_id =
- dex_file_->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
- if (!dex_compilation_unit_->IsStatic()) {
- // Add the implicit 'this' argument, not expressed in the signature.
- HParameterValue* parameter = new (arena_) HParameterValue(*dex_file_,
- referrer_method_id.class_idx_,
- parameter_index++,
- Primitive::kPrimNot,
- true);
- AppendInstruction(parameter);
- UpdateLocal(locals_index++, parameter);
- number_of_parameters--;
- }
-
- const DexFile::ProtoId& proto = dex_file_->GetMethodPrototype(referrer_method_id);
- const DexFile::TypeList* arg_types = dex_file_->GetProtoParameters(proto);
- for (int i = 0, shorty_pos = 1; i < number_of_parameters; i++) {
- HParameterValue* parameter = new (arena_) HParameterValue(
- *dex_file_,
- arg_types->GetTypeItem(shorty_pos - 1).type_idx_,
- parameter_index++,
- Primitive::GetType(shorty[shorty_pos]),
- false);
- ++shorty_pos;
- AppendInstruction(parameter);
- // Store the parameter value in the local that the dex code will use
- // to reference that parameter.
- UpdateLocal(locals_index++, parameter);
- if (Primitive::Is64BitType(parameter->GetType())) {
- i++;
- locals_index++;
- parameter_index++;
- }
- }
-}
-
-template<typename T>
-void HInstructionBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
- HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- T* comparison = new (arena_) T(first, second, dex_pc);
- AppendInstruction(comparison);
- AppendInstruction(new (arena_) HIf(comparison, dex_pc));
- current_block_ = nullptr;
-}
-
-template<typename T>
-void HInstructionBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
- HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
- T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
- AppendInstruction(comparison);
- AppendInstruction(new (arena_) HIf(comparison, dex_pc));
- current_block_ = nullptr;
-}
-
-template<typename T>
-void HInstructionBuilder::Unop_12x(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- AppendInstruction(new (arena_) T(type, first, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-void HInstructionBuilder::Conversion_12x(const Instruction& instruction,
- Primitive::Type input_type,
- Primitive::Type result_type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), input_type);
- AppendInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HInstructionBuilder::Binop_23x(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- HInstruction* second = LoadLocal(instruction.VRegC(), type);
- AppendInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HInstructionBuilder::Binop_23x_shift(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt);
- AppendInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-void HInstructionBuilder::Binop_23x_cmp(const Instruction& instruction,
- Primitive::Type type,
- ComparisonBias bias,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- HInstruction* second = LoadLocal(instruction.VRegC(), type);
- AppendInstruction(new (arena_) HCompare(type, first, second, bias, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HInstructionBuilder::Binop_12x_shift(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegA(), type);
- HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- AppendInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HInstructionBuilder::Binop_12x(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegA(), type);
- HInstruction* second = LoadLocal(instruction.VRegB(), type);
- AppendInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HInstructionBuilder::Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s(), dex_pc);
- if (reverse) {
- std::swap(first, second);
- }
- AppendInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HInstructionBuilder::Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b(), dex_pc);
- if (reverse) {
- std::swap(first, second);
- }
- AppendInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const CompilerDriver& driver) {
- Thread* self = Thread::Current();
- return cu->IsConstructor()
- && driver.RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
-}
-
-// Returns true if `block` has only one successor which starts at the next
-// dex_pc after `instruction` at `dex_pc`.
-static bool IsFallthroughInstruction(const Instruction& instruction,
- uint32_t dex_pc,
- HBasicBlock* block) {
- uint32_t next_dex_pc = dex_pc + instruction.SizeInCodeUnits();
- return block->GetSingleSuccessor()->GetDexPc() == next_dex_pc;
-}
-
-void HInstructionBuilder::BuildSwitch(const Instruction& instruction, uint32_t dex_pc) {
- HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
- DexSwitchTable table(instruction, dex_pc);
-
- if (table.GetNumEntries() == 0) {
- // Empty Switch. Code falls through to the next block.
- DCHECK(IsFallthroughInstruction(instruction, dex_pc, current_block_));
- AppendInstruction(new (arena_) HGoto(dex_pc));
- } else if (table.ShouldBuildDecisionTree()) {
- for (DexSwitchTableIterator it(table); !it.Done(); it.Advance()) {
- HInstruction* case_value = graph_->GetIntConstant(it.CurrentKey(), dex_pc);
- HEqual* comparison = new (arena_) HEqual(value, case_value, dex_pc);
- AppendInstruction(comparison);
- AppendInstruction(new (arena_) HIf(comparison, dex_pc));
-
- if (!it.IsLast()) {
- current_block_ = FindBlockStartingAt(it.GetDexPcForCurrentIndex());
- }
- }
- } else {
- AppendInstruction(
- new (arena_) HPackedSwitch(table.GetEntryAt(0), table.GetNumEntries(), value, dex_pc));
- }
-
- current_block_ = nullptr;
-}
-
-void HInstructionBuilder::BuildReturn(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- if (type == Primitive::kPrimVoid) {
- if (graph_->ShouldGenerateConstructorBarrier()) {
- // The compilation unit is null during testing.
- if (dex_compilation_unit_ != nullptr) {
- DCHECK(RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_))
- << "Inconsistent use of ShouldGenerateConstructorBarrier. Should not generate a barrier.";
- }
- AppendInstruction(new (arena_) HMemoryBarrier(kStoreStore, dex_pc));
- }
- AppendInstruction(new (arena_) HReturnVoid(dex_pc));
- } else {
- HInstruction* value = LoadLocal(instruction.VRegA(), type);
- AppendInstruction(new (arena_) HReturn(value, dex_pc));
- }
- current_block_ = nullptr;
-}
-
-static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) {
- switch (opcode) {
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_STATIC_RANGE:
- return kStatic;
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_DIRECT_RANGE:
- return kDirect;
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_QUICK:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
- return kVirtual;
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_INTERFACE_RANGE:
- return kInterface;
- case Instruction::INVOKE_SUPER_RANGE:
- case Instruction::INVOKE_SUPER:
- return kSuper;
- default:
- LOG(FATAL) << "Unexpected invoke opcode: " << opcode;
- UNREACHABLE();
- }
-}
-
-ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
-
- ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
- Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
-
- ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
- *dex_compilation_unit_->GetDexFile(),
- method_idx,
- dex_compilation_unit_->GetDexCache(),
- class_loader,
- /* referrer */ nullptr,
- invoke_type);
-
- if (UNLIKELY(resolved_method == nullptr)) {
- // Clean up any exception left by type resolution.
- soa.Self()->ClearException();
- return nullptr;
- }
-
- // Check access. The class linker has a fast path for looking into the dex cache
- // and does not check the access if it hits it.
- if (compiling_class.Get() == nullptr) {
- if (!resolved_method->IsPublic()) {
- return nullptr;
- }
- } else if (!compiling_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
- resolved_method,
- dex_compilation_unit_->GetDexCache().Get(),
- method_idx)) {
- return nullptr;
- }
-
- // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not.
- // We need to look at the referrer's super class vtable. We need to do this to know if we need to
- // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
- // which require runtime handling.
- if (invoke_type == kSuper) {
- if (compiling_class.Get() == nullptr) {
- // We could not determine the method's class we need to wait until runtime.
- DCHECK(Runtime::Current()->IsAotCompiler());
- return nullptr;
- }
- ArtMethod* current_method = graph_->GetArtMethod();
- DCHECK(current_method != nullptr);
- Handle<mirror::Class> methods_class(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
- method_idx,
- current_method)));
- if (methods_class.Get() == nullptr) {
- // Invoking a super method requires knowing the actual super class. If we did not resolve
- // the compiling method's declaring class (which only happens for ahead of time
- // compilation), bail out.
- DCHECK(Runtime::Current()->IsAotCompiler());
- return nullptr;
- } else {
- ArtMethod* actual_method;
- if (methods_class->IsInterface()) {
- actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
- resolved_method, class_linker->GetImagePointerSize());
- } else {
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
- vtable_index, class_linker->GetImagePointerSize());
- }
- if (actual_method != resolved_method &&
- !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
- // The back-end code generator relies on this check in order to ensure that it will not
- // attempt to read the dex_cache with a dex_method_index that is not from the correct
- // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
- // builder, which means that the code-generator (and compiler driver during sharpening and
- // inliner, maybe) might invoke an incorrect method.
- // TODO: The actual method could still be referenced in the current dex file, so we
- // could try locating it.
- // TODO: Remove the dex_file restriction.
- return nullptr;
- }
- if (!actual_method->IsInvokable()) {
- // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
- // could resolve the callee to the wrong method.
- return nullptr;
- }
- resolved_method = actual_method;
- }
- }
-
- // Check for incompatible class changes. The class linker has a fast path for
- // looking into the dex cache and does not check incompatible class changes if it hits it.
- if (resolved_method->CheckIncompatibleClassChange(invoke_type)) {
- return nullptr;
- }
-
- return resolved_method;
-}
-
-bool HInstructionBuilder::BuildInvoke(const Instruction& instruction,
- uint32_t dex_pc,
- uint32_t method_idx,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index) {
- InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
- const char* descriptor = dex_file_->GetMethodShorty(method_idx);
- Primitive::Type return_type = Primitive::GetType(descriptor[0]);
-
- // Remove the return type from the 'proto'.
- size_t number_of_arguments = strlen(descriptor) - 1;
- if (invoke_type != kStatic) { // instance call
- // One extra argument for 'this'.
- number_of_arguments++;
- }
-
- MethodReference target_method(dex_file_, method_idx);
-
- // Special handling for string init.
- int32_t string_init_offset = 0;
- bool is_string_init = compiler_driver_->IsStringInit(method_idx,
- dex_file_,
- &string_init_offset);
- // Replace calls to String.<init> with StringFactory.
- if (is_string_init) {
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
- HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- dchecked_integral_cast<uint64_t>(string_init_offset),
- 0U
- };
- HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
- arena_,
- number_of_arguments - 1,
- Primitive::kPrimNot /*return_type */,
- dex_pc,
- method_idx,
- target_method,
- dispatch_info,
- invoke_type,
- kStatic /* optimized_invoke_type */,
- HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
- return HandleStringInit(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor);
- }
-
- ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type);
-
- if (UNLIKELY(resolved_method == nullptr)) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod);
- HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- invoke_type);
- return HandleInvoke(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- nullptr /* clinit_check */);
- }
-
- // Potential class initialization check, in the case of a static method call.
- HClinitCheck* clinit_check = nullptr;
- HInvoke* invoke = nullptr;
- if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) {
- // By default, consider that the called method implicitly requires
- // an initialization check of its declaring method.
- HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement
- = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit;
- ScopedObjectAccess soa(Thread::Current());
- if (invoke_type == kStatic) {
- clinit_check = ProcessClinitCheckForInvoke(
- dex_pc, resolved_method, method_idx, &clinit_check_requirement);
- } else if (invoke_type == kSuper) {
- if (IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
- // Update the target method to the one resolved. Note that this may be a no-op if
- // we resolved to the method referenced by the instruction.
- method_idx = resolved_method->GetDexMethodIndex();
- target_method = MethodReference(dex_file_, method_idx);
- }
- }
-
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
- HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0U
- };
- invoke = new (arena_) HInvokeStaticOrDirect(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- target_method,
- dispatch_info,
- invoke_type,
- invoke_type,
- clinit_check_requirement);
- } else if (invoke_type == kVirtual) {
- ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
- invoke = new (arena_) HInvokeVirtual(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- resolved_method->GetMethodIndex());
- } else {
- DCHECK_EQ(invoke_type, kInterface);
- ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
- invoke = new (arena_) HInvokeInterface(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- resolved_method->GetDexMethodIndex());
- }
-
- return HandleInvoke(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- clinit_check);
-}
-
-bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) {
- bool finalizable;
- bool can_throw = NeedsAccessCheck(type_index, &finalizable);
-
- // Only the non-resolved entrypoint handles the finalizable class case. If we
- // need access checks, then we haven't resolved the method and the class may
- // again be finalizable.
- QuickEntrypointEnum entrypoint = (finalizable || can_throw)
- ? kQuickAllocObject
- : kQuickAllocObjectInitialized;
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
-
- if (outer_dex_cache.Get() != dex_cache.Get()) {
- // We currently do not support inlining allocations across dex files.
- return false;
- }
-
- HLoadClass* load_class = new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- type_index,
- outer_dex_file,
- IsOutermostCompilingClass(type_index),
- dex_pc,
- /*needs_access_check*/ can_throw,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, type_index));
-
- AppendInstruction(load_class);
- HInstruction* cls = load_class;
- if (!IsInitialized(resolved_class)) {
- cls = new (arena_) HClinitCheck(load_class, dex_pc);
- AppendInstruction(cls);
- }
-
- AppendInstruction(new (arena_) HNewInstance(
- cls,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- can_throw,
- finalizable,
- entrypoint));
- return true;
-}
-
-static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class);
-}
-
-bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const {
- if (cls.Get() == nullptr) {
- return false;
- }
-
- // `CanAssumeClassIsLoaded` will return true if we're JITting, or will
- // check whether the class is in an image for the AOT compilation.
- if (cls->IsInitialized() &&
- compiler_driver_->CanAssumeClassIsLoaded(cls.Get())) {
- return true;
- }
-
- if (IsSubClass(GetOutermostCompilingClass(), cls.Get())) {
- return true;
- }
-
- // TODO: We should walk over the inlined methods, but we don't pass
- // that information to the builder.
- if (IsSubClass(GetCompilingClass(), cls.Get())) {
- return true;
- }
-
- return false;
-}
-
-HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke(
- uint32_t dex_pc,
- ArtMethod* resolved_method,
- uint32_t method_idx,
- HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Thread* self = Thread::Current();
- StackHandleScope<4> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- self, *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(
- self, outer_dex_file)));
- Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
- Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass()));
-
- // The index at which the method's class is stored in the DexCache's type array.
- uint32_t storage_index = DexFile::kDexNoIndex;
- bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get());
- if (is_outer_class) {
- storage_index = outer_class->GetDexTypeIndex();
- } else if (outer_dex_cache.Get() == dex_cache.Get()) {
- // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer.
- compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(),
- GetCompilingClass(),
- resolved_method,
- method_idx,
- &storage_index);
- }
-
- HClinitCheck* clinit_check = nullptr;
-
- if (IsInitialized(resolved_method_class)) {
- *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
- } else if (storage_index != DexFile::kDexNoIndex) {
- *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
- HLoadClass* load_class = new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- storage_index,
- outer_dex_file,
- is_outer_class,
- dex_pc,
- /*needs_access_check*/ false,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index));
- AppendInstruction(load_class);
- clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
- AppendInstruction(clinit_check);
- }
- return clinit_check;
-}
-
-bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- size_t start_index,
- size_t* argument_index) {
- uint32_t descriptor_index = 1; // Skip the return type.
-
- for (size_t i = start_index;
- // Make sure we don't go over the expected arguments or over the number of
- // dex registers given. If the instruction was seen as dead by the verifier,
- // it hasn't been properly checked.
- (i < number_of_vreg_arguments) && (*argument_index < invoke->GetNumberOfArguments());
- i++, (*argument_index)++) {
- Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
- bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
- if (!is_range
- && is_wide
- && ((i + 1 == number_of_vreg_arguments) || (args[i] + 1 != args[i + 1]))) {
- // Longs and doubles should be in pairs, that is, sequential registers. The verifier should
- // reject any class where this is violated. However, the verifier only does these checks
- // on non trivially dead instructions, so we just bailout the compilation.
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because of non-sequential dex register pair in wide argument";
- MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
- return false;
- }
- HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
- invoke->SetArgumentAt(*argument_index, arg);
- if (is_wide) {
- i++;
- }
- }
-
- if (*argument_index != invoke->GetNumberOfArguments()) {
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because of wrong number of arguments in invoke instruction";
- MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
- return false;
- }
-
- if (invoke->IsInvokeStaticOrDirect() &&
- HInvokeStaticOrDirect::NeedsCurrentMethodInput(
- invoke->AsInvokeStaticOrDirect()->GetMethodLoadKind())) {
- invoke->SetArgumentAt(*argument_index, graph_->GetCurrentMethod());
- (*argument_index)++;
- }
-
- return true;
-}
-
-bool HInstructionBuilder::HandleInvoke(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- HClinitCheck* clinit_check) {
- DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit());
-
- size_t start_index = 0;
- size_t argument_index = 0;
- if (invoke->GetOriginalInvokeType() != InvokeType::kStatic) { // Instance call.
- HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
- HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
- AppendInstruction(null_check);
- invoke->SetArgumentAt(0, null_check);
- start_index = 1;
- argument_index = 1;
- }
-
- if (!SetupInvokeArguments(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- start_index,
- &argument_index)) {
- return false;
- }
-
- if (clinit_check != nullptr) {
- // Add the class initialization check as last input of `invoke`.
- DCHECK(invoke->IsInvokeStaticOrDirect());
- DCHECK(invoke->AsInvokeStaticOrDirect()->GetClinitCheckRequirement()
- == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit);
- invoke->SetArgumentAt(argument_index, clinit_check);
- argument_index++;
- }
-
- AppendInstruction(invoke);
- latest_result_ = invoke;
-
- return true;
-}
-
-bool HInstructionBuilder::HandleStringInit(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor) {
- DCHECK(invoke->IsInvokeStaticOrDirect());
- DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit());
-
- size_t start_index = 1;
- size_t argument_index = 0;
- if (!SetupInvokeArguments(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- start_index,
- &argument_index)) {
- return false;
- }
-
- AppendInstruction(invoke);
-
- // This is a StringFactory call, not an actual String constructor. Its result
- // replaces the empty String pre-allocated by NewInstance.
- uint32_t orig_this_reg = is_range ? register_index : args[0];
- HInstruction* arg_this = LoadLocal(orig_this_reg, Primitive::kPrimNot);
-
- // Replacing the NewInstance might render it redundant. Keep a list of these
- // to be visited once it is clear whether it is has remaining uses.
- if (arg_this->IsNewInstance()) {
- ssa_builder_->AddUninitializedString(arg_this->AsNewInstance());
- } else {
- DCHECK(arg_this->IsPhi());
- // NewInstance is not the direct input of the StringFactory call. It might
- // be redundant but optimizing this case is not worth the effort.
- }
-
- // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`.
- for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
- if ((*current_locals_)[vreg] == arg_this) {
- (*current_locals_)[vreg] = invoke;
- }
- }
-
- return true;
-}
-
-static Primitive::Type GetFieldAccessType(const DexFile& dex_file, uint16_t field_index) {
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- const char* type = dex_file.GetFieldTypeDescriptor(field_id);
- return Primitive::GetType(type[0]);
-}
-
-bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put) {
- uint32_t source_or_dest_reg = instruction.VRegA_22c();
- uint32_t obj_reg = instruction.VRegB_22c();
- uint16_t field_index;
- if (instruction.IsQuickened()) {
- if (!CanDecodeQuickenedInfo()) {
- return false;
- }
- field_index = LookupQuickenedInfo(dex_pc);
- } else {
- field_index = instruction.VRegC_22c();
- }
-
- ScopedObjectAccess soa(Thread::Current());
- ArtField* resolved_field =
- compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa);
-
-
- HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot);
- HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc);
- AppendInstruction(null_check);
-
- Primitive::Type field_type = (resolved_field == nullptr)
- ? GetFieldAccessType(*dex_file_, field_index)
- : resolved_field->GetTypeAsPrimitiveType();
- if (is_put) {
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
- HInstruction* field_set = nullptr;
- if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check,
- value,
- field_type,
- field_index,
- dex_pc);
- } else {
- uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
- field_set = new (arena_) HInstanceFieldSet(null_check,
- value,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_compilation_unit_->GetDexCache(),
- dex_pc);
- }
- AppendInstruction(field_set);
- } else {
- HInstruction* field_get = nullptr;
- if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check,
- field_type,
- field_index,
- dex_pc);
- } else {
- uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
- field_get = new (arena_) HInstanceFieldGet(null_check,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_compilation_unit_->GetDexCache(),
- dex_pc);
- }
- AppendInstruction(field_get);
- UpdateLocal(source_or_dest_reg, field_get);
- }
-
- return true;
-}
-
-static mirror::Class* GetClassFrom(CompilerDriver* driver,
- const DexCompilationUnit& compilation_unit) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
- const DexFile& dex_file = *compilation_unit.GetDexFile();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader())));
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- compilation_unit.GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
-
- return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
-}
-
-mirror::Class* HInstructionBuilder::GetOutermostCompilingClass() const {
- return GetClassFrom(compiler_driver_, *outer_compilation_unit_);
-}
-
-mirror::Class* HInstructionBuilder::GetCompilingClass() const {
- return GetClassFrom(compiler_driver_, *dex_compilation_unit_);
-}
-
-bool HInstructionBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<4> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
- Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
- soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
- Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
-
- // GetOutermostCompilingClass returns null when the class is unresolved
- // (e.g. if it derives from an unresolved class). This is bogus knowing that
- // we are compiling it.
- // When this happens we cannot establish a direct relation between the current
- // class and the outer class, so we return false.
- // (Note that this is only used for optimizing invokes and field accesses)
- return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get());
-}
-
-void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put,
- Primitive::Type field_type) {
- uint32_t source_or_dest_reg = instruction.VRegA_21c();
- uint16_t field_index = instruction.VRegB_21c();
-
- if (is_put) {
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
- AppendInstruction(
- new (arena_) HUnresolvedStaticFieldSet(value, field_type, field_index, dex_pc));
- } else {
- AppendInstruction(new (arena_) HUnresolvedStaticFieldGet(field_type, field_index, dex_pc));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
- }
-}
-
-bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put) {
- uint32_t source_or_dest_reg = instruction.VRegA_21c();
- uint16_t field_index = instruction.VRegB_21c();
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<5> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
- ArtField* resolved_field = compiler_driver_->ResolveField(
- soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
-
- if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- Primitive::Type field_type = GetFieldAccessType(*dex_file_, field_index);
- BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
- return true;
- }
-
- Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
- Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
-
- // The index at which the field's class is stored in the DexCache's type array.
- uint32_t storage_index;
- bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass());
- if (is_outer_class) {
- storage_index = outer_class->GetDexTypeIndex();
- } else if (outer_dex_cache.Get() != dex_cache.Get()) {
- // The compiler driver cannot currently understand multiple dex caches involved. Just bailout.
- return false;
- } else {
- // TODO: This is rather expensive. Perf it and cache the results if needed.
- std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
- outer_dex_cache.Get(),
- GetCompilingClass(),
- resolved_field,
- field_index,
- &storage_index);
- bool can_easily_access = is_put ? pair.second : pair.first;
- if (!can_easily_access) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedFieldNotAFastAccess);
- BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
- return true;
- }
- }
-
- bool is_in_cache =
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index);
- HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
- storage_index,
- outer_dex_file,
- is_outer_class,
- dex_pc,
- /*needs_access_check*/ false,
- is_in_cache);
- AppendInstruction(constant);
-
- HInstruction* cls = constant;
-
- Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass()));
- if (!IsInitialized(klass)) {
- cls = new (arena_) HClinitCheck(constant, dex_pc);
- AppendInstruction(cls);
- }
-
- uint16_t class_def_index = klass->GetDexClassDefIndex();
- if (is_put) {
- // We need to keep the class alive before loading the value.
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
- DCHECK_EQ(HPhi::ToPhiType(value->GetType()), HPhi::ToPhiType(field_type));
- AppendInstruction(new (arena_) HStaticFieldSet(cls,
- value,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_cache_,
- dex_pc));
- } else {
- AppendInstruction(new (arena_) HStaticFieldGet(cls,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_cache_,
- dex_pc));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
- }
- return true;
-}
-
-void HInstructionBuilder::BuildCheckedDivRem(uint16_t out_vreg,
- uint16_t first_vreg,
- int64_t second_vreg_or_constant,
- uint32_t dex_pc,
- Primitive::Type type,
- bool second_is_constant,
- bool isDiv) {
- DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
-
- HInstruction* first = LoadLocal(first_vreg, type);
- HInstruction* second = nullptr;
- if (second_is_constant) {
- if (type == Primitive::kPrimInt) {
- second = graph_->GetIntConstant(second_vreg_or_constant, dex_pc);
- } else {
- second = graph_->GetLongConstant(second_vreg_or_constant, dex_pc);
- }
- } else {
- second = LoadLocal(second_vreg_or_constant, type);
- }
-
- if (!second_is_constant
- || (type == Primitive::kPrimInt && second->AsIntConstant()->GetValue() == 0)
- || (type == Primitive::kPrimLong && second->AsLongConstant()->GetValue() == 0)) {
- second = new (arena_) HDivZeroCheck(second, dex_pc);
- AppendInstruction(second);
- }
-
- if (isDiv) {
- AppendInstruction(new (arena_) HDiv(type, first, second, dex_pc));
- } else {
- AppendInstruction(new (arena_) HRem(type, first, second, dex_pc));
- }
- UpdateLocal(out_vreg, current_block_->GetLastInstruction());
-}
-
-void HInstructionBuilder::BuildArrayAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put,
- Primitive::Type anticipated_type) {
- uint8_t source_or_dest_reg = instruction.VRegA_23x();
- uint8_t array_reg = instruction.VRegB_23x();
- uint8_t index_reg = instruction.VRegC_23x();
-
- HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
- object = new (arena_) HNullCheck(object, dex_pc);
- AppendInstruction(object);
-
- HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
- AppendInstruction(length);
- HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt);
- index = new (arena_) HBoundsCheck(index, length, dex_pc);
- AppendInstruction(index);
- if (is_put) {
- HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
- // TODO: Insert a type check node if the type is Object.
- HArraySet* aset = new (arena_) HArraySet(object, index, value, anticipated_type, dex_pc);
- ssa_builder_->MaybeAddAmbiguousArraySet(aset);
- AppendInstruction(aset);
- } else {
- HArrayGet* aget = new (arena_) HArrayGet(object, index, anticipated_type, dex_pc);
- ssa_builder_->MaybeAddAmbiguousArrayGet(aget);
- AppendInstruction(aget);
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
- }
- graph_->SetHasBoundsChecks(true);
-}
-
-void HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc,
- uint32_t type_index,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index) {
- HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
- bool finalizable;
- QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
- ? kQuickAllocArrayWithAccessCheck
- : kQuickAllocArray;
- HInstruction* object = new (arena_) HNewArray(length,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- entrypoint);
- AppendInstruction(object);
-
- const char* descriptor = dex_file_->StringByTypeIdx(type_index);
- DCHECK_EQ(descriptor[0], '[') << descriptor;
- char primitive = descriptor[1];
- DCHECK(primitive == 'I'
- || primitive == 'L'
- || primitive == '[') << descriptor;
- bool is_reference_array = (primitive == 'L') || (primitive == '[');
- Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt;
-
- for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
- HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
- HInstruction* index = graph_->GetIntConstant(i, dex_pc);
- HArraySet* aset = new (arena_) HArraySet(object, index, value, type, dex_pc);
- ssa_builder_->MaybeAddAmbiguousArraySet(aset);
- AppendInstruction(aset);
- }
- latest_result_ = object;
-}
-
-template <typename T>
-void HInstructionBuilder::BuildFillArrayData(HInstruction* object,
- const T* data,
- uint32_t element_count,
- Primitive::Type anticipated_type,
- uint32_t dex_pc) {
- for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = graph_->GetIntConstant(i, dex_pc);
- HInstruction* value = graph_->GetIntConstant(data[i], dex_pc);
- HArraySet* aset = new (arena_) HArraySet(object, index, value, anticipated_type, dex_pc);
- ssa_builder_->MaybeAddAmbiguousArraySet(aset);
- AppendInstruction(aset);
- }
-}
-
-void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
- HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot);
- HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
- AppendInstruction(null_check);
-
- HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
- AppendInstruction(length);
-
- int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
- const Instruction::ArrayDataPayload* payload =
- reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item_.insns_ + payload_offset);
- const uint8_t* data = payload->data;
- uint32_t element_count = payload->element_count;
-
- // Implementation of this DEX instruction seems to be that the bounds check is
- // done before doing any stores.
- HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1, dex_pc);
- AppendInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc));
-
- switch (payload->element_width) {
- case 1:
- BuildFillArrayData(null_check,
- reinterpret_cast<const int8_t*>(data),
- element_count,
- Primitive::kPrimByte,
- dex_pc);
- break;
- case 2:
- BuildFillArrayData(null_check,
- reinterpret_cast<const int16_t*>(data),
- element_count,
- Primitive::kPrimShort,
- dex_pc);
- break;
- case 4:
- BuildFillArrayData(null_check,
- reinterpret_cast<const int32_t*>(data),
- element_count,
- Primitive::kPrimInt,
- dex_pc);
- break;
- case 8:
- BuildFillWideArrayData(null_check,
- reinterpret_cast<const int64_t*>(data),
- element_count,
- dex_pc);
- break;
- default:
- LOG(FATAL) << "Unknown element width for " << payload->element_width;
- }
- graph_->SetHasBoundsChecks(true);
-}
-
-void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object,
- const int64_t* data,
- uint32_t element_count,
- uint32_t dex_pc) {
- for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = graph_->GetIntConstant(i, dex_pc);
- HInstruction* value = graph_->GetLongConstant(data[i], dex_pc);
- HArraySet* aset = new (arena_) HArraySet(object, index, value, Primitive::kPrimLong, dex_pc);
- ssa_builder_->MaybeAddAmbiguousArraySet(aset);
- AppendInstruction(aset);
- }
-}
-
-static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- if (cls.Get() == nullptr) {
- return TypeCheckKind::kUnresolvedCheck;
- } else if (cls->IsInterface()) {
- return TypeCheckKind::kInterfaceCheck;
- } else if (cls->IsArrayClass()) {
- if (cls->GetComponentType()->IsObjectClass()) {
- return TypeCheckKind::kArrayObjectCheck;
- } else if (cls->CannotBeAssignedFromOtherTypes()) {
- return TypeCheckKind::kExactCheck;
- } else {
- return TypeCheckKind::kArrayCheck;
- }
- } else if (cls->IsFinal()) {
- return TypeCheckKind::kExactCheck;
- } else if (cls->IsAbstract()) {
- return TypeCheckKind::kAbstractClassCheck;
- } else {
- return TypeCheckKind::kClassHierarchyCheck;
- }
-}
-
-void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
- uint8_t destination,
- uint8_t reference,
- uint16_t type_index,
- uint32_t dex_pc) {
- bool type_known_final, type_known_abstract, use_declaring_class;
- bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(),
- *dex_compilation_unit_->GetDexFile(),
- type_index,
- &type_known_final,
- &type_known_abstract,
- &use_declaring_class);
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
- const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
- Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
-
- HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
- HLoadClass* cls = new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- type_index,
- dex_file,
- IsOutermostCompilingClass(type_index),
- dex_pc,
- !can_access,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index));
- AppendInstruction(cls);
-
- TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
- if (instruction.Opcode() == Instruction::INSTANCE_OF) {
- AppendInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc));
- UpdateLocal(destination, current_block_->GetLastInstruction());
- } else {
- DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
- // We emit a CheckCast followed by a BoundType. CheckCast is a statement
- // which may throw. If it succeeds BoundType sets the new type of `object`
- // for all subsequent uses.
- AppendInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc));
- AppendInstruction(new (arena_) HBoundType(object, dex_pc));
- UpdateLocal(reference, current_block_->GetLastInstruction());
- }
-}
-
-bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const {
- return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, finalizable);
-}
-
-bool HInstructionBuilder::CanDecodeQuickenedInfo() const {
- return interpreter_metadata_ != nullptr;
-}
-
-uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t dex_pc) {
- DCHECK(interpreter_metadata_ != nullptr);
- uint32_t dex_pc_in_map = DecodeUnsignedLeb128(&interpreter_metadata_);
- DCHECK_EQ(dex_pc, dex_pc_in_map);
- return DecodeUnsignedLeb128(&interpreter_metadata_);
-}
-
-bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc) {
- switch (instruction.Opcode()) {
- case Instruction::CONST_4: {
- int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n(), dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- case Instruction::CONST_16: {
- int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s(), dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- case Instruction::CONST: {
- int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i(), dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- case Instruction::CONST_HIGH16: {
- int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16, dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- case Instruction::CONST_WIDE_16: {
- int32_t register_index = instruction.VRegA();
- // Get 16 bits of constant value, sign extended to 64 bits.
- int64_t value = instruction.VRegB_21s();
- value <<= 48;
- value >>= 48;
- HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- case Instruction::CONST_WIDE_32: {
- int32_t register_index = instruction.VRegA();
- // Get 32 bits of constant value, sign extended to 64 bits.
- int64_t value = instruction.VRegB_31i();
- value <<= 32;
- value >>= 32;
- HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- case Instruction::CONST_WIDE: {
- int32_t register_index = instruction.VRegA();
- HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l(), dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- case Instruction::CONST_WIDE_HIGH16: {
- int32_t register_index = instruction.VRegA();
- int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
- HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
- UpdateLocal(register_index, constant);
- break;
- }
-
- // Note that the SSA building will refine the types.
- case Instruction::MOVE:
- case Instruction::MOVE_FROM16:
- case Instruction::MOVE_16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- UpdateLocal(instruction.VRegA(), value);
- break;
- }
-
- // Note that the SSA building will refine the types.
- case Instruction::MOVE_WIDE:
- case Instruction::MOVE_WIDE_FROM16:
- case Instruction::MOVE_WIDE_16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong);
- UpdateLocal(instruction.VRegA(), value);
- break;
- }
-
- case Instruction::MOVE_OBJECT:
- case Instruction::MOVE_OBJECT_16:
- case Instruction::MOVE_OBJECT_FROM16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot);
- UpdateLocal(instruction.VRegA(), value);
- break;
- }
-
- case Instruction::RETURN_VOID_NO_BARRIER:
- case Instruction::RETURN_VOID: {
- BuildReturn(instruction, Primitive::kPrimVoid, dex_pc);
- break;
- }
-
-#define IF_XX(comparison, cond) \
- case Instruction::IF_##cond: If_22t<comparison>(instruction, dex_pc); break; \
- case Instruction::IF_##cond##Z: If_21t<comparison>(instruction, dex_pc); break
-
- IF_XX(HEqual, EQ);
- IF_XX(HNotEqual, NE);
- IF_XX(HLessThan, LT);
- IF_XX(HLessThanOrEqual, LE);
- IF_XX(HGreaterThan, GT);
- IF_XX(HGreaterThanOrEqual, GE);
-
- case Instruction::GOTO:
- case Instruction::GOTO_16:
- case Instruction::GOTO_32: {
- AppendInstruction(new (arena_) HGoto(dex_pc));
- current_block_ = nullptr;
- break;
- }
-
- case Instruction::RETURN: {
- BuildReturn(instruction, return_type_, dex_pc);
- break;
- }
-
- case Instruction::RETURN_OBJECT: {
- BuildReturn(instruction, return_type_, dex_pc);
- break;
- }
-
- case Instruction::RETURN_WIDE: {
- BuildReturn(instruction, return_type_, dex_pc);
- break;
- }
-
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_SUPER:
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_QUICK: {
- uint16_t method_idx;
- if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_QUICK) {
- if (!CanDecodeQuickenedInfo()) {
- return false;
- }
- method_idx = LookupQuickenedInfo(dex_pc);
- } else {
- method_idx = instruction.VRegB_35c();
- }
- uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
- uint32_t args[5];
- instruction.GetVarArgs(args);
- if (!BuildInvoke(instruction, dex_pc, method_idx,
- number_of_vreg_arguments, false, args, -1)) {
- return false;
- }
- break;
- }
-
- case Instruction::INVOKE_DIRECT_RANGE:
- case Instruction::INVOKE_INTERFACE_RANGE:
- case Instruction::INVOKE_STATIC_RANGE:
- case Instruction::INVOKE_SUPER_RANGE:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
- uint16_t method_idx;
- if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK) {
- if (!CanDecodeQuickenedInfo()) {
- return false;
- }
- method_idx = LookupQuickenedInfo(dex_pc);
- } else {
- method_idx = instruction.VRegB_3rc();
- }
- uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
- uint32_t register_index = instruction.VRegC();
- if (!BuildInvoke(instruction, dex_pc, method_idx,
- number_of_vreg_arguments, true, nullptr, register_index)) {
- return false;
- }
- break;
- }
-
- case Instruction::NEG_INT: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::NEG_LONG: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::NEG_FLOAT: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::NEG_DOUBLE: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::NOT_INT: {
- Unop_12x<HNot>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::NOT_LONG: {
- Unop_12x<HNot>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_LONG: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::LONG_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::LONG_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::LONG_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::FLOAT_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::FLOAT_TO_LONG: {
- Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::FLOAT_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::DOUBLE_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::DOUBLE_TO_LONG: {
- Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::DOUBLE_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_BYTE: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_SHORT: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_CHAR: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::ADD_LONG: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_DOUBLE: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::ADD_FLOAT: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_INT: {
- Binop_23x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SUB_LONG: {
- Binop_23x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SUB_FLOAT: {
- Binop_23x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_DOUBLE: {
- Binop_23x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT: {
- Binop_23x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::MUL_LONG: {
- Binop_23x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::MUL_FLOAT: {
- Binop_23x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::MUL_DOUBLE: {
- Binop_23x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::DIV_INT: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, false, true);
- break;
- }
-
- case Instruction::DIV_LONG: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimLong, false, true);
- break;
- }
-
- case Instruction::DIV_FLOAT: {
- Binop_23x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::DIV_DOUBLE: {
- Binop_23x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::REM_INT: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, false, false);
- break;
- }
-
- case Instruction::REM_LONG: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimLong, false, false);
- break;
- }
-
- case Instruction::REM_FLOAT: {
- Binop_23x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::REM_DOUBLE: {
- Binop_23x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::AND_INT: {
- Binop_23x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::AND_LONG: {
- Binop_23x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SHL_INT: {
- Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHL_LONG: {
- Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SHR_INT: {
- Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHR_LONG: {
- Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::USHR_INT: {
- Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::USHR_LONG: {
- Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::OR_INT: {
- Binop_23x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::OR_LONG: {
- Binop_23x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT: {
- Binop_23x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::XOR_LONG: {
- Binop_23x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_LONG_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_DOUBLE_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::ADD_FLOAT_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_INT_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SUB_LONG_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SUB_FLOAT_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_DOUBLE_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::MUL_LONG_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::MUL_FLOAT_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::MUL_DOUBLE_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::DIV_INT_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimInt, false, true);
- break;
- }
-
- case Instruction::DIV_LONG_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimLong, false, true);
- break;
- }
-
- case Instruction::REM_INT_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimInt, false, false);
- break;
- }
-
- case Instruction::REM_LONG_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimLong, false, false);
- break;
- }
-
- case Instruction::REM_FLOAT_2ADDR: {
- Binop_12x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::REM_DOUBLE_2ADDR: {
- Binop_12x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::SHL_INT_2ADDR: {
- Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHL_LONG_2ADDR: {
- Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SHR_INT_2ADDR: {
- Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHR_LONG_2ADDR: {
- Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::USHR_INT_2ADDR: {
- Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::USHR_LONG_2ADDR: {
- Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::DIV_FLOAT_2ADDR: {
- Binop_12x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::DIV_DOUBLE_2ADDR: {
- Binop_12x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::AND_INT_2ADDR: {
- Binop_12x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::AND_LONG_2ADDR: {
- Binop_12x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::OR_INT_2ADDR: {
- Binop_12x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::OR_LONG_2ADDR: {
- Binop_12x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT_2ADDR: {
- Binop_12x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::XOR_LONG_2ADDR: {
- Binop_12x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT_LIT16: {
- Binop_22s<HAdd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::AND_INT_LIT16: {
- Binop_22s<HAnd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::OR_INT_LIT16: {
- Binop_22s<HOr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT_LIT16: {
- Binop_22s<HXor>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::RSUB_INT: {
- Binop_22s<HSub>(instruction, true, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT_LIT16: {
- Binop_22s<HMul>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT_LIT8: {
- Binop_22b<HAdd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::AND_INT_LIT8: {
- Binop_22b<HAnd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::OR_INT_LIT8: {
- Binop_22b<HOr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT_LIT8: {
- Binop_22b<HXor>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::RSUB_INT_LIT8: {
- Binop_22b<HSub>(instruction, true, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT_LIT8: {
- Binop_22b<HMul>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::DIV_INT_LIT16:
- case Instruction::DIV_INT_LIT8: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, true, true);
- break;
- }
-
- case Instruction::REM_INT_LIT16:
- case Instruction::REM_INT_LIT8: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, true, false);
- break;
- }
-
- case Instruction::SHL_INT_LIT8: {
- Binop_22b<HShl>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::SHR_INT_LIT8: {
- Binop_22b<HShr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::USHR_INT_LIT8: {
- Binop_22b<HUShr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::NEW_INSTANCE: {
- if (!BuildNewInstance(instruction.VRegB_21c(), dex_pc)) {
- return false;
- }
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
- break;
- }
-
- case Instruction::NEW_ARRAY: {
- uint16_t type_index = instruction.VRegC_22c();
- HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
- bool finalizable;
- QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
- ? kQuickAllocArrayWithAccessCheck
- : kQuickAllocArray;
- AppendInstruction(new (arena_) HNewArray(length,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- entrypoint));
- UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
- break;
- }
-
- case Instruction::FILLED_NEW_ARRAY: {
- uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
- uint32_t type_index = instruction.VRegB_35c();
- uint32_t args[5];
- instruction.GetVarArgs(args);
- BuildFilledNewArray(dex_pc, type_index, number_of_vreg_arguments, false, args, 0);
- break;
- }
-
- case Instruction::FILLED_NEW_ARRAY_RANGE: {
- uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
- uint32_t type_index = instruction.VRegB_3rc();
- uint32_t register_index = instruction.VRegC_3rc();
- BuildFilledNewArray(
- dex_pc, type_index, number_of_vreg_arguments, true, nullptr, register_index);
- break;
- }
-
- case Instruction::FILL_ARRAY_DATA: {
- BuildFillArrayData(instruction, dex_pc);
- break;
- }
-
- case Instruction::MOVE_RESULT:
- case Instruction::MOVE_RESULT_WIDE:
- case Instruction::MOVE_RESULT_OBJECT: {
- DCHECK(latest_result_ != nullptr);
- UpdateLocal(instruction.VRegA(), latest_result_);
- latest_result_ = nullptr;
- break;
- }
-
- case Instruction::CMP_LONG: {
- Binop_23x_cmp(instruction, Primitive::kPrimLong, ComparisonBias::kNoBias, dex_pc);
- break;
- }
-
- case Instruction::CMPG_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kGtBias, dex_pc);
- break;
- }
-
- case Instruction::CMPG_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kGtBias, dex_pc);
- break;
- }
-
- case Instruction::CMPL_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kLtBias, dex_pc);
- break;
- }
-
- case Instruction::CMPL_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kLtBias, dex_pc);
- break;
- }
-
- case Instruction::NOP:
- break;
-
- case Instruction::IGET:
- case Instruction::IGET_QUICK:
- case Instruction::IGET_WIDE:
- case Instruction::IGET_WIDE_QUICK:
- case Instruction::IGET_OBJECT:
- case Instruction::IGET_OBJECT_QUICK:
- case Instruction::IGET_BOOLEAN:
- case Instruction::IGET_BOOLEAN_QUICK:
- case Instruction::IGET_BYTE:
- case Instruction::IGET_BYTE_QUICK:
- case Instruction::IGET_CHAR:
- case Instruction::IGET_CHAR_QUICK:
- case Instruction::IGET_SHORT:
- case Instruction::IGET_SHORT_QUICK: {
- if (!BuildInstanceFieldAccess(instruction, dex_pc, false)) {
- return false;
- }
- break;
- }
-
- case Instruction::IPUT:
- case Instruction::IPUT_QUICK:
- case Instruction::IPUT_WIDE:
- case Instruction::IPUT_WIDE_QUICK:
- case Instruction::IPUT_OBJECT:
- case Instruction::IPUT_OBJECT_QUICK:
- case Instruction::IPUT_BOOLEAN:
- case Instruction::IPUT_BOOLEAN_QUICK:
- case Instruction::IPUT_BYTE:
- case Instruction::IPUT_BYTE_QUICK:
- case Instruction::IPUT_CHAR:
- case Instruction::IPUT_CHAR_QUICK:
- case Instruction::IPUT_SHORT:
- case Instruction::IPUT_SHORT_QUICK: {
- if (!BuildInstanceFieldAccess(instruction, dex_pc, true)) {
- return false;
- }
- break;
- }
-
- case Instruction::SGET:
- case Instruction::SGET_WIDE:
- case Instruction::SGET_OBJECT:
- case Instruction::SGET_BOOLEAN:
- case Instruction::SGET_BYTE:
- case Instruction::SGET_CHAR:
- case Instruction::SGET_SHORT: {
- if (!BuildStaticFieldAccess(instruction, dex_pc, false)) {
- return false;
- }
- break;
- }
-
- case Instruction::SPUT:
- case Instruction::SPUT_WIDE:
- case Instruction::SPUT_OBJECT:
- case Instruction::SPUT_BOOLEAN:
- case Instruction::SPUT_BYTE:
- case Instruction::SPUT_CHAR:
- case Instruction::SPUT_SHORT: {
- if (!BuildStaticFieldAccess(instruction, dex_pc, true)) {
- return false;
- }
- break;
- }
-
-#define ARRAY_XX(kind, anticipated_type) \
- case Instruction::AGET##kind: { \
- BuildArrayAccess(instruction, dex_pc, false, anticipated_type); \
- break; \
- } \
- case Instruction::APUT##kind: { \
- BuildArrayAccess(instruction, dex_pc, true, anticipated_type); \
- break; \
- }
-
- ARRAY_XX(, Primitive::kPrimInt);
- ARRAY_XX(_WIDE, Primitive::kPrimLong);
- ARRAY_XX(_OBJECT, Primitive::kPrimNot);
- ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean);
- ARRAY_XX(_BYTE, Primitive::kPrimByte);
- ARRAY_XX(_CHAR, Primitive::kPrimChar);
- ARRAY_XX(_SHORT, Primitive::kPrimShort);
-
- case Instruction::ARRAY_LENGTH: {
- HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
- object = new (arena_) HNullCheck(object, dex_pc);
- AppendInstruction(object);
- AppendInstruction(new (arena_) HArrayLength(object, dex_pc));
- UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
- break;
- }
-
- case Instruction::CONST_STRING: {
- uint32_t string_index = instruction.VRegB_21c();
- AppendInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
- UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
- break;
- }
-
- case Instruction::CONST_STRING_JUMBO: {
- uint32_t string_index = instruction.VRegB_31c();
- AppendInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
- UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction());
- break;
- }
-
- case Instruction::CONST_CLASS: {
- uint16_t type_index = instruction.VRegB_21c();
- bool type_known_final;
- bool type_known_abstract;
- bool dont_use_is_referrers_class;
- // `CanAccessTypeWithoutChecks` will tell whether the method being
- // built is trying to access its own class, so that the generated
- // code can optimize for this case. However, the optimization does not
- // work for inlining, so we use `IsOutermostCompilingClass` instead.
- bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
- &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
- AppendInstruction(new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- type_index,
- *dex_file_,
- IsOutermostCompilingClass(type_index),
- dex_pc,
- !can_access,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(*dex_file_, type_index)));
- UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
- break;
- }
-
- case Instruction::MOVE_EXCEPTION: {
- AppendInstruction(new (arena_) HLoadException(dex_pc));
- UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
- AppendInstruction(new (arena_) HClearException(dex_pc));
- break;
- }
-
- case Instruction::THROW: {
- HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot);
- AppendInstruction(new (arena_) HThrow(exception, dex_pc));
- // We finished building this block. Set the current block to null to avoid
- // adding dead instructions to it.
- current_block_ = nullptr;
- break;
- }
-
- case Instruction::INSTANCE_OF: {
- uint8_t destination = instruction.VRegA_22c();
- uint8_t reference = instruction.VRegB_22c();
- uint16_t type_index = instruction.VRegC_22c();
- BuildTypeCheck(instruction, destination, reference, type_index, dex_pc);
- break;
- }
-
- case Instruction::CHECK_CAST: {
- uint8_t reference = instruction.VRegA_21c();
- uint16_t type_index = instruction.VRegB_21c();
- BuildTypeCheck(instruction, -1, reference, type_index, dex_pc);
- break;
- }
-
- case Instruction::MONITOR_ENTER: {
- AppendInstruction(new (arena_) HMonitorOperation(
- LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
- HMonitorOperation::OperationKind::kEnter,
- dex_pc));
- break;
- }
-
- case Instruction::MONITOR_EXIT: {
- AppendInstruction(new (arena_) HMonitorOperation(
- LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
- HMonitorOperation::OperationKind::kExit,
- dex_pc));
- break;
- }
-
- case Instruction::SPARSE_SWITCH:
- case Instruction::PACKED_SWITCH: {
- BuildSwitch(instruction, dex_pc);
- break;
- }
-
- default:
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because of unhandled instruction "
- << instruction.Name();
- MaybeRecordStat(MethodCompilationStat::kNotCompiledUnhandledInstruction);
- return false;
- }
- return true;
-} // NOLINT(readability/fn_size)
-
-} // namespace art
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
deleted file mode 100644
index 612594f..0000000
--- a/compiler/optimizing/instruction_builder.h
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
-#define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
-
-#include "base/arena_containers.h"
-#include "base/arena_object.h"
-#include "block_builder.h"
-#include "driver/compiler_driver.h"
-#include "driver/compiler_driver-inl.h"
-#include "driver/dex_compilation_unit.h"
-#include "mirror/dex_cache.h"
-#include "nodes.h"
-#include "optimizing_compiler_stats.h"
-#include "ssa_builder.h"
-
-namespace art {
-
-class HInstructionBuilder : public ValueObject {
- public:
- HInstructionBuilder(HGraph* graph,
- HBasicBlockBuilder* block_builder,
- SsaBuilder* ssa_builder,
- const DexFile* dex_file,
- const DexFile::CodeItem& code_item,
- Primitive::Type return_type,
- DexCompilationUnit* dex_compilation_unit,
- const DexCompilationUnit* const outer_compilation_unit,
- CompilerDriver* driver,
- const uint8_t* interpreter_metadata,
- OptimizingCompilerStats* compiler_stats,
- Handle<mirror::DexCache> dex_cache)
- : arena_(graph->GetArena()),
- graph_(graph),
- dex_file_(dex_file),
- code_item_(code_item),
- return_type_(return_type),
- block_builder_(block_builder),
- ssa_builder_(ssa_builder),
- locals_for_(arena_->Adapter(kArenaAllocGraphBuilder)),
- current_block_(nullptr),
- current_locals_(nullptr),
- latest_result_(nullptr),
- compiler_driver_(driver),
- dex_compilation_unit_(dex_compilation_unit),
- outer_compilation_unit_(outer_compilation_unit),
- interpreter_metadata_(interpreter_metadata),
- compilation_stats_(compiler_stats),
- dex_cache_(dex_cache),
- loop_headers_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)) {
- loop_headers_.reserve(kDefaultNumberOfLoops);
- }
-
- bool Build();
-
- private:
- void MaybeRecordStat(MethodCompilationStat compilation_stat);
-
- void InitializeBlockLocals();
- void PropagateLocalsToCatchBlocks();
- void SetLoopHeaderPhiInputs();
-
- bool ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc);
- void FindNativeDebugInfoLocations(ArenaBitVector* locations);
-
- bool CanDecodeQuickenedInfo() const;
- uint16_t LookupQuickenedInfo(uint32_t dex_pc);
-
- HBasicBlock* FindBlockStartingAt(uint32_t dex_pc) const;
-
- ArenaVector<HInstruction*>* GetLocalsFor(HBasicBlock* block);
- HInstruction* ValueOfLocalAt(HBasicBlock* block, size_t local);
- HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type) const;
- void UpdateLocal(uint32_t register_index, HInstruction* instruction);
-
- void AppendInstruction(HInstruction* instruction);
- void InsertInstructionAtTop(HInstruction* instruction);
- void InitializeInstruction(HInstruction* instruction);
-
- void InitializeParameters();
-
- // Returns whether the current method needs access check for the type.
- // Output parameter finalizable is set to whether the type is finalizable.
- bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const;
-
- template<typename T>
- void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_23x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_23x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- void Binop_23x_cmp(const Instruction& instruction,
- Primitive::Type type,
- ComparisonBias bias,
- uint32_t dex_pc);
-
- template<typename T>
- void Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_12x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc);
-
- template<typename T>
- void Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc);
-
- template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_pc);
- template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_pc);
-
- void Conversion_12x(const Instruction& instruction,
- Primitive::Type input_type,
- Primitive::Type result_type,
- uint32_t dex_pc);
-
- void BuildCheckedDivRem(uint16_t out_reg,
- uint16_t first_reg,
- int64_t second_reg_or_constant,
- uint32_t dex_pc,
- Primitive::Type type,
- bool second_is_lit,
- bool is_div);
-
- void BuildReturn(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- // Builds an instance field access node and returns whether the instruction is supported.
- bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
-
- void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put,
- Primitive::Type field_type);
- // Builds a static field access node and returns whether the instruction is supported.
- bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
-
- void BuildArrayAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_get,
- Primitive::Type anticipated_type);
-
- // Builds an invocation node and returns whether the instruction is supported.
- bool BuildInvoke(const Instruction& instruction,
- uint32_t dex_pc,
- uint32_t method_idx,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index);
-
- // Builds a new array node and the instructions that fill it.
- void BuildFilledNewArray(uint32_t dex_pc,
- uint32_t type_index,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index);
-
- void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc);
-
- // Fills the given object with data as specified in the fill-array-data
- // instruction. Currently only used for non-reference and non-floating point
- // arrays.
- template <typename T>
- void BuildFillArrayData(HInstruction* object,
- const T* data,
- uint32_t element_count,
- Primitive::Type anticipated_type,
- uint32_t dex_pc);
-
- // Fills the given object with data as specified in the fill-array-data
- // instruction. The data must be for long and double arrays.
- void BuildFillWideArrayData(HInstruction* object,
- const int64_t* data,
- uint32_t element_count,
- uint32_t dex_pc);
-
- // Builds a `HInstanceOf`, or a `HCheckCast` instruction.
- void BuildTypeCheck(const Instruction& instruction,
- uint8_t destination,
- uint8_t reference,
- uint16_t type_index,
- uint32_t dex_pc);
-
- // Builds an instruction sequence for a switch statement.
- void BuildSwitch(const Instruction& instruction, uint32_t dex_pc);
-
- // Returns the outer-most compiling method's class.
- mirror::Class* GetOutermostCompilingClass() const;
-
- // Returns the class whose method is being compiled.
- mirror::Class* GetCompilingClass() const;
-
- // Returns whether `type_index` points to the outer-most compiling method's class.
- bool IsOutermostCompilingClass(uint16_t type_index) const;
-
- void PotentiallySimplifyFakeString(uint16_t original_dex_register,
- uint32_t dex_pc,
- HInvoke* invoke);
-
- bool SetupInvokeArguments(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- size_t start_index,
- size_t* argument_index);
-
- bool HandleInvoke(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- HClinitCheck* clinit_check);
-
- bool HandleStringInit(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor);
- void HandleStringInitResult(HInvokeStaticOrDirect* invoke);
-
- HClinitCheck* ProcessClinitCheckForInvoke(
- uint32_t dex_pc,
- ArtMethod* method,
- uint32_t method_idx,
- HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- // Build a HNewInstance instruction.
- bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc);
-
- // Return whether the compiler can assume `cls` is initialized.
- bool IsInitialized(Handle<mirror::Class> cls) const
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- // Try to resolve a method using the class linker. Return null if a method could
- // not be resolved.
- ArtMethod* ResolveMethod(uint16_t method_idx, InvokeType invoke_type);
-
- ArenaAllocator* const arena_;
- HGraph* const graph_;
-
- // The dex file where the method being compiled is, and the bytecode data.
- const DexFile* const dex_file_;
- const DexFile::CodeItem& code_item_;
-
- // The return type of the method being compiled.
- const Primitive::Type return_type_;
-
- HBasicBlockBuilder* block_builder_;
- SsaBuilder* ssa_builder_;
-
- ArenaVector<ArenaVector<HInstruction*>> locals_for_;
- HBasicBlock* current_block_;
- ArenaVector<HInstruction*>* current_locals_;
- HInstruction* latest_result_;
-
- CompilerDriver* const compiler_driver_;
-
- // The compilation unit of the current method being compiled. Note that
- // it can be an inlined method.
- DexCompilationUnit* const dex_compilation_unit_;
-
- // The compilation unit of the outermost method being compiled. That is the
- // method being compiled (and not inlined), and potentially inlining other
- // methods.
- const DexCompilationUnit* const outer_compilation_unit_;
-
- const uint8_t* interpreter_metadata_;
- OptimizingCompilerStats* compilation_stats_;
- Handle<mirror::DexCache> dex_cache_;
-
- ArenaVector<HBasicBlock*> loop_headers_;
-
- static constexpr int kDefaultNumberOfLoops = 2;
-
- DISALLOW_COPY_AND_ASSIGN(HInstructionBuilder);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index bdaef1d..3202493 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -211,8 +211,8 @@
*
* Which becomes the following graph (numbered by lifetime position):
* 2: constant0
- * 4: constant5
- * 6: constant4
+ * 4: constant4
+ * 6: constant5
* 8: goto
* |
* 12: goto
@@ -247,7 +247,7 @@
liveness.Analyze();
// Test for the 0 constant.
- LiveInterval* interval = graph->GetIntConstant(0)->GetLiveInterval();
+ LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
LiveRange* range = interval->GetFirstRange();
ASSERT_EQ(2u, range->GetStart());
// Last use is the loop phi so instruction is live until
@@ -256,18 +256,18 @@
ASSERT_TRUE(range->GetNext() == nullptr);
// Test for the 4 constant.
- interval = graph->GetIntConstant(4)->GetLiveInterval();
+ interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval();
range = interval->GetFirstRange();
// The instruction is live until the end of the loop.
- ASSERT_EQ(6u, range->GetStart());
+ ASSERT_EQ(4u, range->GetStart());
ASSERT_EQ(24u, range->GetEnd());
ASSERT_TRUE(range->GetNext() == nullptr);
// Test for the 5 constant.
- interval = graph->GetIntConstant(5)->GetLiveInterval();
+ interval = liveness.GetInstructionFromSsaIndex(2)->GetLiveInterval();
range = interval->GetFirstRange();
// The instruction is live until the return instruction after the loop.
- ASSERT_EQ(4u, range->GetStart());
+ ASSERT_EQ(6u, range->GetStart());
ASSERT_EQ(26u, range->GetEnd());
ASSERT_TRUE(range->GetNext() == nullptr);
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index bd74368..92a987c 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -154,7 +154,7 @@
// return a;
//
// Bitsets are made of:
- // (constant0, constant5, constant4, phi)
+ // (constant0, constant4, constant5, phi)
const char* expected =
"Block 0\n" // entry block
" live in: (0000)\n"
@@ -165,11 +165,11 @@
" live out: (0110)\n"
" kill: (0000)\n"
"Block 2\n" // else block
- " live in: (0010)\n"
+ " live in: (0100)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 3\n" // then block
- " live in: (0100)\n"
+ " live in: (0010)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 4\n" // return block
@@ -291,7 +291,7 @@
// }
// return 5;
// Bitsets are made of:
- // (constant0, constant5, constant4, phi)
+ // (constant0, constant4, constant5, phi)
const char* expected =
"Block 0\n"
" live in: (0000)\n"
@@ -310,7 +310,7 @@
" live out: (0110)\n"
" kill: (0000)\n"
"Block 4\n" // return block
- " live in: (0100)\n"
+ " live in: (0010)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 5\n" // exit block
@@ -386,7 +386,7 @@
// Make sure we create a preheader of a loop when a header originally has two
// incoming blocks and one back edge.
// Bitsets are made of:
- // (constant0, constant5, constant4, phi in block 8)
+ // (constant0, constant4, constant5, phi in block 8)
const char* expected =
"Block 0\n"
" live in: (0000)\n"
@@ -397,11 +397,11 @@
" live out: (0110)\n"
" kill: (0000)\n"
"Block 2\n"
- " live in: (0010)\n"
+ " live in: (0100)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 3\n"
- " live in: (0100)\n"
+ " live in: (0010)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 4\n" // loop header
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 1086cbf..9f448af 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -318,11 +318,21 @@
}
}
+ // Place the suspend check at the beginning of the header, so that live registers
+ // will be known when allocating registers. Note that code generation can still
+ // generate the suspend check at the back edge, but needs to be careful with
+ // loop phi spill slots (which are not written to at back edge).
HInstruction* first_instruction = header->GetFirstInstruction();
- if (first_instruction != nullptr && first_instruction->IsSuspendCheck()) {
- // Called from DeadBlockElimination. Update SuspendCheck pointer.
- info->SetSuspendCheck(first_instruction->AsSuspendCheck());
+ if (first_instruction == nullptr) {
+ HSuspendCheck* check = new (arena_) HSuspendCheck(header->GetDexPc());
+ header->AddInstruction(check);
+ first_instruction = check;
+ } else if (!first_instruction->IsSuspendCheck()) {
+ HSuspendCheck* check = new (arena_) HSuspendCheck(header->GetDexPc());
+ header->InsertInstructionBefore(check, first_instruction);
+ first_instruction = check;
}
+ info->SetSuspendCheck(first_instruction->AsSuspendCheck());
}
void HGraph::ComputeTryBlockInformation() {
@@ -1872,7 +1882,6 @@
instr_it.Advance()) {
HInstruction* current = instr_it.Current();
if (current->NeedsEnvironment()) {
- DCHECK(current->HasEnvironment());
current->GetEnvironment()->SetAndCopyParentChain(
outer_graph->GetArena(), invoke->GetEnvironment());
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 0088fed..8a2e83a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -427,10 +427,6 @@
number_of_in_vregs_ = value;
}
- uint16_t GetNumberOfInVRegs() const {
- return number_of_in_vregs_;
- }
-
uint16_t GetNumberOfLocalVRegs() const {
DCHECK(!in_ssa_form_);
return number_of_vregs_ - number_of_in_vregs_;
@@ -1215,7 +1211,9 @@
M(LessThanOrEqual, Condition) \
M(LoadClass, Instruction) \
M(LoadException, Instruction) \
+ M(LoadLocal, Instruction) \
M(LoadString, Instruction) \
+ M(Local, Instruction) \
M(LongConstant, Constant) \
M(MemoryBarrier, Instruction) \
M(MonitorOperation, Instruction) \
@@ -1246,6 +1244,7 @@
M(UnresolvedStaticFieldGet, Instruction) \
M(UnresolvedStaticFieldSet, Instruction) \
M(Select, Instruction) \
+ M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
M(Throw, Instruction) \
@@ -2384,107 +2383,6 @@
DISALLOW_COPY_AND_ASSIGN(HReturn);
};
-class HPhi : public HInstruction {
- public:
- HPhi(ArenaAllocator* arena,
- uint32_t reg_number,
- size_t number_of_inputs,
- Primitive::Type type,
- uint32_t dex_pc = kNoDexPc)
- : HInstruction(SideEffects::None(), dex_pc),
- inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
- reg_number_(reg_number) {
- SetPackedField<TypeField>(ToPhiType(type));
- DCHECK_NE(GetType(), Primitive::kPrimVoid);
- // Phis are constructed live and marked dead if conflicting or unused.
- // Individual steps of SsaBuilder should assume that if a phi has been
- // marked dead, it can be ignored and will be removed by SsaPhiElimination.
- SetPackedFlag<kFlagIsLive>(true);
- SetPackedFlag<kFlagCanBeNull>(true);
- }
-
- // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
- static Primitive::Type ToPhiType(Primitive::Type type) {
- return Primitive::PrimitiveKind(type);
- }
-
- bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
-
- size_t InputCount() const OVERRIDE { return inputs_.size(); }
-
- void AddInput(HInstruction* input);
- void RemoveInputAt(size_t index);
-
- Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
- void SetType(Primitive::Type new_type) {
- // Make sure that only valid type changes occur. The following are allowed:
- // (1) int -> float/ref (primitive type propagation),
- // (2) long -> double (primitive type propagation).
- DCHECK(GetType() == new_type ||
- (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
- (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
- (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
- SetPackedField<TypeField>(new_type);
- }
-
- bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
- void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
-
- uint32_t GetRegNumber() const { return reg_number_; }
-
- void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
- void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
- bool IsDead() const { return !IsLive(); }
- bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
-
- bool IsVRegEquivalentOf(HInstruction* other) const {
- return other != nullptr
- && other->IsPhi()
- && other->AsPhi()->GetBlock() == GetBlock()
- && other->AsPhi()->GetRegNumber() == GetRegNumber();
- }
-
- // Returns the next equivalent phi (starting from the current one) or null if there is none.
- // An equivalent phi is a phi having the same dex register and type.
- // It assumes that phis with the same dex register are adjacent.
- HPhi* GetNextEquivalentPhiWithSameType() {
- HInstruction* next = GetNext();
- while (next != nullptr && next->AsPhi()->GetRegNumber() == reg_number_) {
- if (next->GetType() == GetType()) {
- return next->AsPhi();
- }
- next = next->GetNext();
- }
- return nullptr;
- }
-
- DECLARE_INSTRUCTION(Phi);
-
- protected:
- const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
- return inputs_[index];
- }
-
- void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
- inputs_[index] = input;
- }
-
- private:
- static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
- static constexpr size_t kFieldTypeSize =
- MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
- static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
- static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
- static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
- static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
- using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
-
- ArenaVector<HUserRecord<HInstruction*> > inputs_;
- const uint32_t reg_number_;
-
- DISALLOW_COPY_AND_ASSIGN(HPhi);
-};
-
// The exit instruction is the only instruction of the exit block.
// Instructions aborting the method (HThrow and HReturn) must branch to the
// exit block.
@@ -3645,6 +3543,57 @@
DISALLOW_COPY_AND_ASSIGN(HCompare);
};
+// A local in the graph. Corresponds to a Dex register.
+class HLocal : public HTemplateInstruction<0> {
+ public:
+ explicit HLocal(uint16_t reg_number)
+ : HTemplateInstruction(SideEffects::None(), kNoDexPc), reg_number_(reg_number) {}
+
+ DECLARE_INSTRUCTION(Local);
+
+ uint16_t GetRegNumber() const { return reg_number_; }
+
+ private:
+ // The Dex register number.
+ const uint16_t reg_number_;
+
+ DISALLOW_COPY_AND_ASSIGN(HLocal);
+};
+
+// Load a given local. The local is an input of this instruction.
+class HLoadLocal : public HExpression<1> {
+ public:
+ HLoadLocal(HLocal* local, Primitive::Type type, uint32_t dex_pc = kNoDexPc)
+ : HExpression(type, SideEffects::None(), dex_pc) {
+ SetRawInputAt(0, local);
+ }
+
+ HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); }
+
+ DECLARE_INSTRUCTION(LoadLocal);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HLoadLocal);
+};
+
+// Store a value in a given local. This instruction has two inputs: the value
+// and the local.
+class HStoreLocal : public HTemplateInstruction<2> {
+ public:
+ HStoreLocal(HLocal* local, HInstruction* value, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
+ SetRawInputAt(0, local);
+ SetRawInputAt(1, value);
+ }
+
+ HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); }
+
+ DECLARE_INSTRUCTION(StoreLocal);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HStoreLocal);
+};
+
class HNewInstance : public HExpression<2> {
public:
HNewInstance(HInstruction* cls,
@@ -3965,7 +3914,8 @@
// potentially one other if the clinit check is explicit, and potentially
// one other if the method is a string factory.
(NeedsCurrentMethodInput(dispatch_info.method_load_kind) ? 1u : 0u) +
- (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u),
+ (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u) +
+ (dispatch_info.method_load_kind == MethodLoadKind::kStringInit ? 1u : 0u),
return_type,
dex_pc,
method_index,
@@ -4093,6 +4043,15 @@
DCHECK(!IsStaticWithExplicitClinitCheck());
}
+ HInstruction* GetAndRemoveThisArgumentOfStringInit() {
+ DCHECK(IsStringInit());
+ size_t index = InputCount() - 1;
+ HInstruction* input = InputAt(index);
+ RemoveAsUserOfInput(index);
+ inputs_.pop_back();
+ return input;
+ }
+
// Is this a call to a static method whose declaring class has an
// explicit initialization check in the graph?
bool IsStaticWithExplicitClinitCheck() const {
@@ -4935,6 +4894,7 @@
SideEffectsForArchRuntimeCalls(input->GetType(), result_type),
dex_pc) {
SetRawInputAt(0, input);
+ DCHECK_NE(input->GetType(), result_type);
// Invariant: We should never generate a conversion to a Boolean value.
DCHECK_NE(Primitive::kPrimBoolean, result_type);
}
@@ -4970,6 +4930,115 @@
static constexpr uint32_t kNoRegNumber = -1;
+class HPhi : public HInstruction {
+ public:
+ HPhi(ArenaAllocator* arena,
+ uint32_t reg_number,
+ size_t number_of_inputs,
+ Primitive::Type type,
+ uint32_t dex_pc = kNoDexPc)
+ : HInstruction(SideEffects::None(), dex_pc),
+ inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
+ reg_number_(reg_number) {
+ SetPackedField<TypeField>(ToPhiType(type));
+ DCHECK_NE(GetType(), Primitive::kPrimVoid);
+ // Phis are constructed live and marked dead if conflicting or unused.
+ // Individual steps of SsaBuilder should assume that if a phi has been
+ // marked dead, it can be ignored and will be removed by SsaPhiElimination.
+ SetPackedFlag<kFlagIsLive>(true);
+ SetPackedFlag<kFlagCanBeNull>(true);
+ }
+
+ // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
+ static Primitive::Type ToPhiType(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ return Primitive::kPrimInt;
+ default:
+ return type;
+ }
+ }
+
+ bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
+
+ size_t InputCount() const OVERRIDE { return inputs_.size(); }
+
+ void AddInput(HInstruction* input);
+ void RemoveInputAt(size_t index);
+
+ Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
+ void SetType(Primitive::Type new_type) {
+ // Make sure that only valid type changes occur. The following are allowed:
+ // (1) int -> float/ref (primitive type propagation),
+ // (2) long -> double (primitive type propagation).
+ DCHECK(GetType() == new_type ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
+ (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
+ SetPackedField<TypeField>(new_type);
+ }
+
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
+ void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
+
+ uint32_t GetRegNumber() const { return reg_number_; }
+
+ void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
+ void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
+ bool IsDead() const { return !IsLive(); }
+ bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
+
+ bool IsVRegEquivalentOf(HInstruction* other) const {
+ return other != nullptr
+ && other->IsPhi()
+ && other->AsPhi()->GetBlock() == GetBlock()
+ && other->AsPhi()->GetRegNumber() == GetRegNumber();
+ }
+
+ // Returns the next equivalent phi (starting from the current one) or null if there is none.
+ // An equivalent phi is a phi having the same dex register and type.
+ // It assumes that phis with the same dex register are adjacent.
+ HPhi* GetNextEquivalentPhiWithSameType() {
+ HInstruction* next = GetNext();
+ while (next != nullptr && next->AsPhi()->GetRegNumber() == reg_number_) {
+ if (next->GetType() == GetType()) {
+ return next->AsPhi();
+ }
+ next = next->GetNext();
+ }
+ return nullptr;
+ }
+
+ DECLARE_INSTRUCTION(Phi);
+
+ protected:
+ const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
+ return inputs_[index];
+ }
+
+ void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
+ inputs_[index] = input;
+ }
+
+ private:
+ static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
+ static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
+ static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
+
+ ArenaVector<HUserRecord<HInstruction*> > inputs_;
+ const uint32_t reg_number_;
+
+ DISALLOW_COPY_AND_ASSIGN(HPhi);
+};
+
class HNullCheck : public HExpression<1> {
public:
// `HNullCheck` can trigger GC, as it may call the `NullPointerException`
@@ -5313,7 +5382,7 @@
// constructor.
HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
: HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
- DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
+ DCHECK(index->GetType() == Primitive::kPrimInt);
SetRawInputAt(0, index);
SetRawInputAt(1, length);
}
@@ -5844,7 +5913,7 @@
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
- DCHECK_EQ(Primitive::PrimitiveKind(field_type), Primitive::PrimitiveKind(value->GetType()));
+ DCHECK_EQ(field_type, value->GetType());
SetRawInputAt(0, obj);
SetRawInputAt(1, value);
}
@@ -5904,7 +5973,7 @@
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
- DCHECK_EQ(Primitive::PrimitiveKind(field_type), Primitive::PrimitiveKind(value->GetType()));
+ DCHECK_EQ(field_type, value->GetType());
SetRawInputAt(0, value);
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3d6bf62..20a6661 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -731,9 +731,8 @@
compiler_driver,
compilation_stats_.get(),
interpreter_metadata,
- dex_cache,
- &handles);
- GraphAnalysisResult result = builder.BuildGraph();
+ dex_cache);
+ GraphAnalysisResult result = builder.BuildGraph(&handles);
if (result != kAnalysisSuccess) {
switch (result) {
case kAnalysisSkipped:
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index dd5cb1c..b140125 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -91,8 +91,8 @@
{
ScopedObjectAccess soa(Thread::Current());
StackHandleScopeCollection handles(soa.Self());
- HGraphBuilder builder(graph, *item, &handles, return_type);
- bool graph_built = (builder.BuildGraph() == kAnalysisSuccess);
+ HGraphBuilder builder(graph, *item, return_type);
+ bool graph_built = (builder.BuildGraph(&handles) == kAnalysisSuccess);
return graph_built ? graph : nullptr;
}
}
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 951cdfb..a444688 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -104,9 +104,9 @@
"BasicBlock 1, pred: 0, succ: 3\n"
" 2: Goto 3\n"
"BasicBlock 2, pred: 3, succ: 4\n"
- " 4: ReturnVoid\n"
+ " 3: ReturnVoid\n"
"BasicBlock 3, pred: 1, succ: 2\n"
- " 3: Goto 2\n"
+ " 4: Goto 2\n"
"BasicBlock 4, pred: 2\n"
" 5: Exit\n";
@@ -135,10 +135,10 @@
TEST_F(PrettyPrinterTest, CFG4) {
const char* expected =
"BasicBlock 0, succ: 3\n"
- " 1: SuspendCheck\n"
- " 2: Goto 3\n"
+ " 2: SuspendCheck\n"
+ " 3: Goto 3\n"
"BasicBlock 1, pred: 3, 1, succ: 1\n"
- " 3: SuspendCheck\n"
+ " 1: SuspendCheck\n"
" 4: Goto 1\n"
"BasicBlock 3, pred: 0, succ: 1\n"
" 0: Goto 1\n";
@@ -176,18 +176,18 @@
TEST_F(PrettyPrinterTest, CFG6) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 3: IntConstant [4, 4]\n"
- " 1: SuspendCheck\n"
- " 2: Goto 1\n"
+ " 4: IntConstant [8, 8]\n"
+ " 2: SuspendCheck\n"
+ " 3: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5, 2\n"
- " 4: Equal(3, 3) [5]\n"
- " 5: If(4)\n"
+ " 8: Equal(4, 4) [9]\n"
+ " 9: If(8)\n"
"BasicBlock 2, pred: 1, succ: 3\n"
- " 6: Goto 3\n"
+ " 10: Goto 3\n"
"BasicBlock 3, pred: 5, 2, succ: 4\n"
- " 7: ReturnVoid\n"
+ " 11: ReturnVoid\n"
"BasicBlock 4, pred: 3\n"
- " 8: Exit\n"
+ " 12: Exit\n"
"BasicBlock 5, pred: 1, succ: 3\n"
" 0: Goto 3\n";
@@ -203,17 +203,17 @@
TEST_F(PrettyPrinterTest, CFG7) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 4: IntConstant [5, 5]\n"
- " 2: SuspendCheck\n"
- " 3: Goto 1\n"
+ " 6: IntConstant [10, 10]\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5, 6\n"
- " 5: Equal(4, 4) [6]\n"
- " 6: If(5)\n"
+ " 10: Equal(6, 6) [11]\n"
+ " 11: If(10)\n"
"BasicBlock 2, pred: 6, 3, succ: 3\n"
- " 11: Goto 3\n"
+ " 12: Goto 3\n"
"BasicBlock 3, pred: 5, 2, succ: 2\n"
- " 8: SuspendCheck\n"
- " 9: Goto 2\n"
+ " 2: SuspendCheck\n"
+ " 13: Goto 2\n"
"BasicBlock 5, pred: 1, succ: 3\n"
" 0: Goto 3\n"
"BasicBlock 6, pred: 1, succ: 2\n"
@@ -231,13 +231,13 @@
TEST_F(PrettyPrinterTest, IntConstant) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 3: IntConstant\n"
+ " 1: SuspendCheck\n"
+ " 2: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 3: ReturnVoid\n"
+ " 5: ReturnVoid\n"
"BasicBlock 2, pred: 1\n"
- " 4: Exit\n";
+ " 6: Exit\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index eeadbeb..5a05256 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -23,9 +23,30 @@
namespace art {
+void SsaBuilder::SetLoopHeaderPhiInputs() {
+ for (size_t i = loop_headers_.size(); i > 0; --i) {
+ HBasicBlock* block = loop_headers_[i - 1];
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ size_t vreg = phi->GetRegNumber();
+ for (HBasicBlock* predecessor : block->GetPredecessors()) {
+ HInstruction* value = ValueOfLocal(predecessor, vreg);
+ if (value == nullptr) {
+ // Vreg is undefined at this predecessor. Mark it dead and leave with
+ // fewer inputs than predecessors. SsaChecker will fail if not removed.
+ phi->SetDead();
+ break;
+ } else {
+ phi->AddInput(value);
+ }
+ }
+ }
+ }
+}
+
void SsaBuilder::FixNullConstantType() {
// The order doesn't matter here.
- for (HReversePostOrderIterator itb(*graph_); !itb.Done(); itb.Advance()) {
+ for (HReversePostOrderIterator itb(*GetGraph()); !itb.Done(); itb.Advance()) {
for (HInstructionIterator it(itb.Current()->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* equality_instr = it.Current();
if (!equality_instr->IsEqual() && !equality_instr->IsNotEqual()) {
@@ -50,14 +71,14 @@
// can only be the 0 constant.
DCHECK(int_operand->IsIntConstant()) << int_operand->DebugName();
DCHECK_EQ(0, int_operand->AsIntConstant()->GetValue());
- equality_instr->ReplaceInput(graph_->GetNullConstant(), int_operand == right ? 1 : 0);
+ equality_instr->ReplaceInput(GetGraph()->GetNullConstant(), int_operand == right ? 1 : 0);
}
}
}
void SsaBuilder::EquivalentPhisCleanup() {
// The order doesn't matter here.
- for (HReversePostOrderIterator itb(*graph_); !itb.Done(); itb.Advance()) {
+ for (HReversePostOrderIterator itb(*GetGraph()); !itb.Done(); itb.Advance()) {
for (HInstructionIterator it(itb.Current()->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
HPhi* next = phi->GetNextEquivalentPhiWithSameType();
@@ -79,7 +100,7 @@
}
void SsaBuilder::FixEnvironmentPhis() {
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
for (HInstructionIterator it_phis(block->GetPhis()); !it_phis.Done(); it_phis.Advance()) {
HPhi* phi = it_phis.Current()->AsPhi();
@@ -233,9 +254,9 @@
}
void SsaBuilder::RunPrimitiveTypePropagation() {
- ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter());
+ ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter());
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (block->IsLoopHeader()) {
for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
@@ -279,14 +300,8 @@
static HArrayGet* FindFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
Primitive::Type type = aget->GetType();
DCHECK(Primitive::IsIntOrLongType(type));
- HInstruction* next = aget->GetNext();
- if (next != nullptr && next->IsArrayGet()) {
- HArrayGet* next_aget = next->AsArrayGet();
- if (next_aget->IsEquivalentOf(aget)) {
- return next_aget;
- }
- }
- return nullptr;
+ HArrayGet* next = aget->GetNext()->AsArrayGet();
+ return (next != nullptr && next->IsEquivalentOf(aget)) ? next : nullptr;
}
static HArrayGet* CreateFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
@@ -319,7 +334,7 @@
// uses (because they are untyped) and environment uses (if --debuggable).
// After resolving all ambiguous ArrayGets, we will re-run primitive type
// propagation on the Phis which need to be updated.
- ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter());
+ ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter());
{
ScopedObjectAccess soa(Thread::Current());
@@ -437,7 +452,7 @@
}
void SsaBuilder::RemoveRedundantUninitializedStrings() {
- if (graph_->IsDebuggable()) {
+ if (GetGraph()->IsDebuggable()) {
// Do not perform the optimization for consistency with the interpreter
// which always allocates an object for new-instance of String.
return;
@@ -445,13 +460,11 @@
for (HNewInstance* new_instance : uninitialized_strings_) {
DCHECK(new_instance->IsInBlock());
- DCHECK(new_instance->IsStringAlloc());
-
// Replace NewInstance of String with NullConstant if not used prior to
// calling StringFactory. In case of deoptimization, the interpreter is
// expected to skip null check on the `this` argument of the StringFactory call.
if (!new_instance->HasNonEnvironmentUses() && !HasAliasInEnvironments(new_instance)) {
- new_instance->ReplaceWith(graph_->GetNullConstant());
+ new_instance->ReplaceWith(GetGraph()->GetNullConstant());
new_instance->GetBlock()->RemoveInstruction(new_instance);
// Remove LoadClass if not needed any more.
@@ -482,47 +495,57 @@
}
GraphAnalysisResult SsaBuilder::BuildSsa() {
- DCHECK(!graph_->IsInSsaForm());
+ DCHECK(!GetGraph()->IsInSsaForm());
- // 1) Propagate types of phis. At this point, phis are typed void in the general
+ // 1) Visit in reverse post order. We need to have all predecessors of a block
+ // visited (with the exception of loops) in order to create the right environment
+ // for that block. For loops, we create phis whose inputs will be set in 2).
+ for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+
+ // 2) Set inputs of loop header phis.
+ SetLoopHeaderPhiInputs();
+
+ // 3) Propagate types of phis. At this point, phis are typed void in the general
// case, or float/double/reference if we created an equivalent phi. So we need
// to propagate the types across phis to give them a correct type. If a type
// conflict is detected in this stage, the phi is marked dead.
RunPrimitiveTypePropagation();
- // 2) Now that the correct primitive types have been assigned, we can get rid
+ // 4) Now that the correct primitive types have been assigned, we can get rid
// of redundant phis. Note that we cannot do this phase before type propagation,
// otherwise we could get rid of phi equivalents, whose presence is a requirement
// for the type propagation phase. Note that this is to satisfy statement (a)
// of the SsaBuilder (see ssa_builder.h).
- SsaRedundantPhiElimination(graph_).Run();
+ SsaRedundantPhiElimination(GetGraph()).Run();
- // 3) Fix the type for null constants which are part of an equality comparison.
+ // 5) Fix the type for null constants which are part of an equality comparison.
// We need to do this after redundant phi elimination, to ensure the only cases
// that we can see are reference comparison against 0. The redundant phi
// elimination ensures we do not see a phi taking two 0 constants in a HEqual
// or HNotEqual.
FixNullConstantType();
- // 4) Compute type of reference type instructions. The pass assumes that
+ // 6) Compute type of reference type instructions. The pass assumes that
// NullConstant has been fixed up.
- ReferenceTypePropagation(graph_, handles_, /* is_first_run */ true).Run();
+ ReferenceTypePropagation(GetGraph(), handles_, /* is_first_run */ true).Run();
- // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type
- // (int/float or long/double) and marked ArraySets with ambiguous input type.
- // Now that RTP computed the type of the array input, the ambiguity can be
- // resolved and the correct equivalents kept.
+ // 7) Step 1) duplicated ArrayGet instructions with ambiguous type (int/float
+ // or long/double) and marked ArraySets with ambiguous input type. Now that RTP
+ // computed the type of the array input, the ambiguity can be resolved and the
+ // correct equivalents kept.
if (!FixAmbiguousArrayOps()) {
return kAnalysisFailAmbiguousArrayOp;
}
- // 6) Mark dead phis. This will mark phis which are not used by instructions
+ // 8) Mark dead phis. This will mark phis which are not used by instructions
// or other live phis. If compiling as debuggable code, phis will also be kept
// live if they have an environment use.
- SsaDeadPhiElimination dead_phi_elimimation(graph_);
+ SsaDeadPhiElimination dead_phi_elimimation(GetGraph());
dead_phi_elimimation.MarkDeadPhis();
- // 7) Make sure environments use the right phi equivalent: a phi marked dead
+ // 9) Make sure environments use the right phi equivalent: a phi marked dead
// can have a phi equivalent that is not dead. In that case we have to replace
// it with the live equivalent because deoptimization and try/catch rely on
// environments containing values of all live vregs at that point. Note that
@@ -531,26 +554,165 @@
// environments to just reference one.
FixEnvironmentPhis();
- // 8) Now that the right phis are used for the environments, we can eliminate
+ // 10) Now that the right phis are used for the environments, we can eliminate
// phis we do not need. Regardless of the debuggable status, this phase is
/// necessary for statement (b) of the SsaBuilder (see ssa_builder.h), as well
// as for the code generation, which does not deal with phis of conflicting
// input types.
dead_phi_elimimation.EliminateDeadPhis();
- // 9) HInstructionBuidler replaced uses of NewInstances of String with the
- // results of their corresponding StringFactory calls. Unless the String
- // objects are used before they are initialized, they can be replaced with
- // NullConstant. Note that this optimization is valid only if unsimplified
- // code does not use the uninitialized value because we assume execution can
- // be deoptimized at any safepoint. We must therefore perform it before any
- // other optimizations.
+ // 11) Step 1) replaced uses of NewInstances of String with the results of
+ // their corresponding StringFactory calls. Unless the String objects are used
+ // before they are initialized, they can be replaced with NullConstant.
+ // Note that this optimization is valid only if unsimplified code does not use
+ // the uninitialized value because we assume execution can be deoptimized at
+ // any safepoint. We must therefore perform it before any other optimizations.
RemoveRedundantUninitializedStrings();
- graph_->SetInSsaForm();
+ // 12) Clear locals.
+ for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions());
+ !it.Done();
+ it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->IsLocal()) {
+ current->GetBlock()->RemoveInstruction(current);
+ }
+ }
+
+ GetGraph()->SetInSsaForm();
return kAnalysisSuccess;
}
+ArenaVector<HInstruction*>* SsaBuilder::GetLocalsFor(HBasicBlock* block) {
+ ArenaVector<HInstruction*>* locals = &locals_for_[block->GetBlockId()];
+ const size_t vregs = GetGraph()->GetNumberOfVRegs();
+ if (locals->empty() && vregs != 0u) {
+ locals->resize(vregs, nullptr);
+
+ if (block->IsCatchBlock()) {
+ ArenaAllocator* arena = GetGraph()->GetArena();
+ // We record incoming inputs of catch phis at throwing instructions and
+ // must therefore eagerly create the phis. Phis for undefined vregs will
+ // be deleted when the first throwing instruction with the vreg undefined
+ // is encountered. Unused phis will be removed by dead phi analysis.
+ for (size_t i = 0; i < vregs; ++i) {
+ // No point in creating the catch phi if it is already undefined at
+ // the first throwing instruction.
+ HInstruction* current_local_value = (*current_locals_)[i];
+ if (current_local_value != nullptr) {
+ HPhi* phi = new (arena) HPhi(
+ arena,
+ i,
+ 0,
+ current_local_value->GetType());
+ block->AddPhi(phi);
+ (*locals)[i] = phi;
+ }
+ }
+ }
+ }
+ return locals;
+}
+
+HInstruction* SsaBuilder::ValueOfLocal(HBasicBlock* block, size_t local) {
+ ArenaVector<HInstruction*>* locals = GetLocalsFor(block);
+ return (*locals)[local];
+}
+
+void SsaBuilder::VisitBasicBlock(HBasicBlock* block) {
+ current_locals_ = GetLocalsFor(block);
+
+ if (block->IsCatchBlock()) {
+ // Catch phis were already created and inputs collected from throwing sites.
+ if (kIsDebugBuild) {
+ // Make sure there was at least one throwing instruction which initialized
+ // locals (guaranteed by HGraphBuilder) and that all try blocks have been
+ // visited already (from HTryBoundary scoping and reverse post order).
+ bool catch_block_visited = false;
+ for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+ HBasicBlock* current = it.Current();
+ if (current == block) {
+ catch_block_visited = true;
+ } else if (current->IsTryBlock() &&
+ current->GetTryCatchInformation()->GetTryEntry().HasExceptionHandler(*block)) {
+ DCHECK(!catch_block_visited) << "Catch block visited before its try block.";
+ }
+ }
+ DCHECK_EQ(current_locals_->size(), GetGraph()->GetNumberOfVRegs())
+ << "No instructions throwing into a live catch block.";
+ }
+ } else if (block->IsLoopHeader()) {
+ // If the block is a loop header, we know we only have visited the pre header
+ // because we are visiting in reverse post order. We create phis for all initialized
+ // locals from the pre header. Their inputs will be populated at the end of
+ // the analysis.
+ for (size_t local = 0; local < current_locals_->size(); ++local) {
+ HInstruction* incoming = ValueOfLocal(block->GetLoopInformation()->GetPreHeader(), local);
+ if (incoming != nullptr) {
+ HPhi* phi = new (GetGraph()->GetArena()) HPhi(
+ GetGraph()->GetArena(),
+ local,
+ 0,
+ incoming->GetType());
+ block->AddPhi(phi);
+ (*current_locals_)[local] = phi;
+ }
+ }
+ // Save the loop header so that the last phase of the analysis knows which
+ // blocks need to be updated.
+ loop_headers_.push_back(block);
+ } else if (block->GetPredecessors().size() > 0) {
+ // All predecessors have already been visited because we are visiting in reverse post order.
+ // We merge the values of all locals, creating phis if those values differ.
+ for (size_t local = 0; local < current_locals_->size(); ++local) {
+ bool one_predecessor_has_no_value = false;
+ bool is_different = false;
+ HInstruction* value = ValueOfLocal(block->GetPredecessors()[0], local);
+
+ for (HBasicBlock* predecessor : block->GetPredecessors()) {
+ HInstruction* current = ValueOfLocal(predecessor, local);
+ if (current == nullptr) {
+ one_predecessor_has_no_value = true;
+ break;
+ } else if (current != value) {
+ is_different = true;
+ }
+ }
+
+ if (one_predecessor_has_no_value) {
+ // If one predecessor has no value for this local, we trust the verifier has
+ // successfully checked that there is a store dominating any read after this block.
+ continue;
+ }
+
+ if (is_different) {
+ HInstruction* first_input = ValueOfLocal(block->GetPredecessors()[0], local);
+ HPhi* phi = new (GetGraph()->GetArena()) HPhi(
+ GetGraph()->GetArena(),
+ local,
+ block->GetPredecessors().size(),
+ first_input->GetType());
+ for (size_t i = 0; i < block->GetPredecessors().size(); i++) {
+ HInstruction* pred_value = ValueOfLocal(block->GetPredecessors()[i], local);
+ phi->SetRawInputAt(i, pred_value);
+ }
+ block->AddPhi(phi);
+ value = phi;
+ }
+ (*current_locals_)[local] = value;
+ }
+ }
+
+ // Visit all instructions. The instructions of interest are:
+ // - HLoadLocal: replace them with the current value of the local.
+ // - HStoreLocal: update current value of the local and remove the instruction.
+ // - Instructions that require an environment: populate their environment
+ // with the current values of the locals.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ it.Current()->Accept(this);
+ }
+}
+
/**
* Constants in the Dex format are not typed. So the builder types them as
* integers, but when doing the SSA form, we might realize the constant
@@ -561,10 +723,11 @@
// We place the floating point constant next to this constant.
HFloatConstant* result = constant->GetNext()->AsFloatConstant();
if (result == nullptr) {
- float value = bit_cast<float, int32_t>(constant->GetValue());
- result = new (graph_->GetArena()) HFloatConstant(value);
+ HGraph* graph = constant->GetBlock()->GetGraph();
+ ArenaAllocator* allocator = graph->GetArena();
+ result = new (allocator) HFloatConstant(bit_cast<float, int32_t>(constant->GetValue()));
constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext());
- graph_->CacheFloatConstant(result);
+ graph->CacheFloatConstant(result);
} else {
// If there is already a constant with the expected type, we know it is
// the floating point equivalent of this constant.
@@ -583,10 +746,11 @@
// We place the floating point constant next to this constant.
HDoubleConstant* result = constant->GetNext()->AsDoubleConstant();
if (result == nullptr) {
- double value = bit_cast<double, int64_t>(constant->GetValue());
- result = new (graph_->GetArena()) HDoubleConstant(value);
+ HGraph* graph = constant->GetBlock()->GetGraph();
+ ArenaAllocator* allocator = graph->GetArena();
+ result = new (allocator) HDoubleConstant(bit_cast<double, int64_t>(constant->GetValue()));
constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext());
- graph_->CacheDoubleConstant(result);
+ graph->CacheDoubleConstant(result);
} else {
// If there is already a constant with the expected type, we know it is
// the floating point equivalent of this constant.
@@ -617,7 +781,7 @@
if (next == nullptr
|| (next->AsPhi()->GetRegNumber() != phi->GetRegNumber())
|| (next->GetType() != type)) {
- ArenaAllocator* allocator = graph_->GetArena();
+ ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena();
HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type);
for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
// Copy the inputs. Note that the graph may not be correctly typed
@@ -677,7 +841,7 @@
HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) {
if (value->IsIntConstant() && value->AsIntConstant()->GetValue() == 0) {
- return graph_->GetNullConstant();
+ return value->GetBlock()->GetGraph()->GetNullConstant();
} else if (value->IsPhi()) {
return GetFloatDoubleOrReferenceEquivalentOfPhi(value->AsPhi(), Primitive::kPrimNot);
} else {
@@ -685,4 +849,171 @@
}
}
+void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {
+ Primitive::Type load_type = load->GetType();
+ HInstruction* value = (*current_locals_)[load->GetLocal()->GetRegNumber()];
+ // If the operation requests a specific type, we make sure its input is of that type.
+ if (load_type != value->GetType()) {
+ if (load_type == Primitive::kPrimFloat || load_type == Primitive::kPrimDouble) {
+ value = GetFloatOrDoubleEquivalent(value, load_type);
+ } else if (load_type == Primitive::kPrimNot) {
+ value = GetReferenceTypeEquivalent(value);
+ }
+ }
+
+ load->ReplaceWith(value);
+ load->GetBlock()->RemoveInstruction(load);
+}
+
+void SsaBuilder::VisitStoreLocal(HStoreLocal* store) {
+ uint32_t reg_number = store->GetLocal()->GetRegNumber();
+ HInstruction* stored_value = store->InputAt(1);
+ Primitive::Type stored_type = stored_value->GetType();
+ DCHECK_NE(stored_type, Primitive::kPrimVoid);
+
+ // Storing into vreg `reg_number` may implicitly invalidate the surrounding
+ // registers. Consider the following cases:
+ // (1) Storing a wide value must overwrite previous values in both `reg_number`
+ // and `reg_number+1`. We store `nullptr` in `reg_number+1`.
+ // (2) If vreg `reg_number-1` holds a wide value, writing into `reg_number`
+ // must invalidate it. We store `nullptr` in `reg_number-1`.
+ // Consequently, storing a wide value into the high vreg of another wide value
+ // will invalidate both `reg_number-1` and `reg_number+1`.
+
+ if (reg_number != 0) {
+ HInstruction* local_low = (*current_locals_)[reg_number - 1];
+ if (local_low != nullptr && Primitive::Is64BitType(local_low->GetType())) {
+ // The vreg we are storing into was previously the high vreg of a pair.
+ // We need to invalidate its low vreg.
+ DCHECK((*current_locals_)[reg_number] == nullptr);
+ (*current_locals_)[reg_number - 1] = nullptr;
+ }
+ }
+
+ (*current_locals_)[reg_number] = stored_value;
+ if (Primitive::Is64BitType(stored_type)) {
+ // We are storing a pair. Invalidate the instruction in the high vreg.
+ (*current_locals_)[reg_number + 1] = nullptr;
+ }
+
+ store->GetBlock()->RemoveInstruction(store);
+}
+
+bool SsaBuilder::IsFirstAtThrowingDexPc(HInstruction* instruction) const {
+ uint32_t dex_pc = instruction->GetDexPc();
+ if (dex_pc == kNoDexPc) {
+ return false;
+ }
+
+ // Needs to be the first HInstruction with this dex_pc.
+ HInstruction* previous = instruction->GetPrevious();
+ if (previous != nullptr && previous->GetDexPc() == dex_pc) {
+ return false;
+ }
+
+ if (instruction->IsControlFlow() && !instruction->IsThrow()) {
+ // Special-case non-throwing control-flow HInstruction because artifically
+ // created ones are given dex_pc of the nearest bytecode instructions.
+ return false;
+ }
+
+ return IsThrowingDexInstruction(GetDexInstructionAt(code_item_, dex_pc));
+}
+
+void SsaBuilder::VisitInstruction(HInstruction* instruction) {
+ if (instruction->NeedsEnvironment()) {
+ HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment(
+ GetGraph()->GetArena(),
+ current_locals_->size(),
+ GetGraph()->GetDexFile(),
+ GetGraph()->GetMethodIdx(),
+ instruction->GetDexPc(),
+ GetGraph()->GetInvokeType(),
+ instruction);
+ environment->CopyFrom(*current_locals_);
+ instruction->SetRawEnvironment(environment);
+ }
+
+ // If in a try block, propagate values of locals into catch blocks.
+ if (instruction->GetBlock()->IsTryBlock() && IsFirstAtThrowingDexPc(instruction)) {
+ const HTryBoundary& try_entry =
+ instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
+ for (HBasicBlock* catch_block : try_entry.GetExceptionHandlers()) {
+ ArenaVector<HInstruction*>* handler_locals = GetLocalsFor(catch_block);
+ DCHECK_EQ(handler_locals->size(), current_locals_->size());
+ for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
+ HInstruction* handler_value = (*handler_locals)[vreg];
+ if (handler_value == nullptr) {
+ // Vreg was undefined at a previously encountered throwing instruction
+ // and the catch phi was deleted. Do not record the local value.
+ continue;
+ }
+ DCHECK(handler_value->IsPhi());
+
+ HInstruction* local_value = (*current_locals_)[vreg];
+ if (local_value == nullptr) {
+ // This is the first instruction throwing into `catch_block` where
+ // `vreg` is undefined. Delete the catch phi.
+ catch_block->RemovePhi(handler_value->AsPhi());
+ (*handler_locals)[vreg] = nullptr;
+ } else {
+ // Vreg has been defined at all instructions throwing into `catch_block`
+ // encountered so far. Record the local value in the catch phi.
+ handler_value->AsPhi()->AddInput(local_value);
+ }
+ }
+ }
+ }
+}
+
+void SsaBuilder::VisitArrayGet(HArrayGet* aget) {
+ Primitive::Type type = aget->GetType();
+ DCHECK(!Primitive::IsFloatingPointType(type));
+ if (Primitive::IsIntOrLongType(type)) {
+ ambiguous_agets_.push_back(aget);
+ }
+ VisitInstruction(aget);
+}
+
+void SsaBuilder::VisitArraySet(HArraySet* aset) {
+ Primitive::Type type = aset->GetValue()->GetType();
+ if (Primitive::IsIntOrLongType(type)) {
+ ambiguous_asets_.push_back(aset);
+ }
+ VisitInstruction(aset);
+}
+
+void SsaBuilder::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ VisitInstruction(invoke);
+
+ if (invoke->IsStringInit()) {
+ // This is a StringFactory call which acts as a String constructor. Its
+ // result replaces the empty String pre-allocated by NewInstance.
+ HInstruction* arg_this = invoke->GetAndRemoveThisArgumentOfStringInit();
+
+ // Replacing the NewInstance might render it redundant. Keep a list of these
+ // to be visited once it is clear whether it is has remaining uses.
+ if (arg_this->IsNewInstance()) {
+ HNewInstance* new_instance = arg_this->AsNewInstance();
+ // Note that in some rare cases (b/27847265), the same NewInstance may be seen
+ // multiple times. We should only consider it once for removal, so we
+ // ensure it is not added more than once.
+ if (!ContainsElement(uninitialized_strings_, new_instance)) {
+ uninitialized_strings_.push_back(new_instance);
+ }
+ } else {
+ DCHECK(arg_this->IsPhi());
+ // NewInstance is not the direct input of the StringFactory call. It might
+ // be redundant but optimizing this case is not worth the effort.
+ }
+
+ // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`.
+ for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
+ if ((*current_locals_)[vreg] == arg_this) {
+ (*current_locals_)[vreg] = invoke;
+ }
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index c37c28c..83da378 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -23,6 +23,8 @@
namespace art {
+static constexpr int kDefaultNumberOfLoops = 2;
+
/**
* Transforms a graph into SSA form. The liveness guarantees of
* this transformation are listed below. A DEX register
@@ -45,48 +47,38 @@
* is not set, values of Dex registers only used by environments
* are killed.
*/
-class SsaBuilder : public ValueObject {
+class SsaBuilder : public HGraphVisitor {
public:
- SsaBuilder(HGraph* graph, StackHandleScopeCollection* handles)
- : graph_(graph),
+ SsaBuilder(HGraph* graph, const DexFile::CodeItem& code_item, StackHandleScopeCollection* handles)
+ : HGraphVisitor(graph),
+ code_item_(code_item),
handles_(handles),
agets_fixed_(false),
- ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
- ambiguous_asets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
- uninitialized_strings_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)) {
- graph_->InitializeInexactObjectRTI(handles);
+ current_locals_(nullptr),
+ loop_headers_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
+ ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
+ ambiguous_asets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
+ uninitialized_strings_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
+ locals_for_(graph->GetBlocks().size(),
+ ArenaVector<HInstruction*>(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
+ graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) {
+ loop_headers_.reserve(kDefaultNumberOfLoops);
}
GraphAnalysisResult BuildSsa();
- HInstruction* GetFloatOrDoubleEquivalent(HInstruction* instruction, Primitive::Type type);
- HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction);
+ // Returns locals vector for `block`. If it is a catch block, the vector will be
+ // prepopulated with catch phis for vregs which are defined in `current_locals_`.
+ ArenaVector<HInstruction*>* GetLocalsFor(HBasicBlock* block);
+ HInstruction* ValueOfLocal(HBasicBlock* block, size_t local);
- void MaybeAddAmbiguousArrayGet(HArrayGet* aget) {
- Primitive::Type type = aget->GetType();
- DCHECK(!Primitive::IsFloatingPointType(type));
- if (Primitive::IsIntOrLongType(type)) {
- ambiguous_agets_.push_back(aget);
- }
- }
-
- void MaybeAddAmbiguousArraySet(HArraySet* aset) {
- Primitive::Type type = aset->GetValue()->GetType();
- if (Primitive::IsIntOrLongType(type)) {
- ambiguous_asets_.push_back(aset);
- }
- }
-
- void AddUninitializedString(HNewInstance* string) {
- // In some rare cases (b/27847265), the same NewInstance may be seen
- // multiple times. We should only consider it once for removal, so we
- // ensure it is not added more than once.
- // Note that we cannot check whether this really is a NewInstance of String
- // before RTP. We DCHECK that in RemoveRedundantUninitializedStrings.
- if (!ContainsElement(uninitialized_strings_, string)) {
- uninitialized_strings_.push_back(string);
- }
- }
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+ void VisitLoadLocal(HLoadLocal* load) OVERRIDE;
+ void VisitStoreLocal(HStoreLocal* store) OVERRIDE;
+ void VisitInstruction(HInstruction* instruction) OVERRIDE;
+ void VisitArrayGet(HArrayGet* aget) OVERRIDE;
+ void VisitArraySet(HArraySet* aset) OVERRIDE;
+ void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
private:
void SetLoopHeaderPhiInputs();
@@ -104,6 +96,9 @@
bool UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist);
void ProcessPrimitiveTypePropagationWorklist(ArenaVector<HPhi*>* worklist);
+ HInstruction* GetFloatOrDoubleEquivalent(HInstruction* instruction, Primitive::Type type);
+ HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction);
+
HFloatConstant* GetFloatEquivalent(HIntConstant* constant);
HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant);
HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type);
@@ -111,16 +106,30 @@
void RemoveRedundantUninitializedStrings();
- HGraph* graph_;
+ // Returns whether `instruction` is the first generated HInstruction for its
+ // dex_pc position.
+ bool IsFirstAtThrowingDexPc(HInstruction* instruction) const;
+
+ const DexFile::CodeItem& code_item_;
StackHandleScopeCollection* const handles_;
// True if types of ambiguous ArrayGets have been resolved.
bool agets_fixed_;
+ // Locals for the current block being visited.
+ ArenaVector<HInstruction*>* current_locals_;
+
+ // Keep track of loop headers found. The last phase of the analysis iterates
+ // over these blocks to set the inputs of their phis.
+ ArenaVector<HBasicBlock*> loop_headers_;
+
ArenaVector<HArrayGet*> ambiguous_agets_;
ArenaVector<HArraySet*> ambiguous_asets_;
ArenaVector<HNewInstance*> uninitialized_strings_;
+ // HEnvironment for each block.
+ ArenaVector<ArenaVector<HInstruction*>> locals_for_;
+
DISALLOW_COPY_AND_ASSIGN(SsaBuilder);
};
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 218bd53..a688092 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -163,8 +163,8 @@
const char* expected =
"BasicBlock 0, succ: 1\n"
" 0: IntConstant 0 [4, 4]\n"
- " 1: IntConstant 5 [8]\n"
- " 2: IntConstant 4 [8]\n"
+ " 1: IntConstant 4 [8]\n"
+ " 2: IntConstant 5 [8]\n"
" 3: Goto\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 4: Equal(0, 0) [5]\n"
@@ -174,7 +174,7 @@
"BasicBlock 3, pred: 1, succ: 4\n"
" 7: Goto\n"
"BasicBlock 4, pred: 2, 3, succ: 5\n"
- " 8: Phi(2, 1) [9]\n"
+ " 8: Phi(1, 2) [9]\n"
" 9: Return(8)\n"
"BasicBlock 5, pred: 4\n"
" 10: Exit\n";
@@ -258,19 +258,19 @@
const char* expected =
"BasicBlock 0, succ: 1\n"
" 0: IntConstant 0 [5]\n"
- " 1: IntConstant 5 [9]\n"
- " 2: IntConstant 4 [5]\n"
+ " 1: IntConstant 4 [5]\n"
+ " 2: IntConstant 5 [9]\n"
" 3: Goto\n"
"BasicBlock 1, pred: 0, succ: 2\n"
" 4: Goto\n"
"BasicBlock 2, pred: 1, 3, succ: 4, 3\n"
- " 5: Phi(0, 2) [6, 6]\n"
+ " 5: Phi(0, 1) [6, 6]\n"
" 6: Equal(5, 5) [7]\n"
" 7: If(6)\n"
"BasicBlock 3, pred: 2, succ: 2\n"
" 8: Goto\n"
"BasicBlock 4, pred: 2, succ: 5\n"
- " 9: Return(1)\n"
+ " 9: Return(2)\n"
"BasicBlock 5, pred: 4\n"
" 10: Exit\n";
@@ -326,8 +326,8 @@
const char* expected =
"BasicBlock 0, succ: 1\n"
" 0: IntConstant 0 [4, 4]\n"
- " 1: IntConstant 5 [13]\n"
- " 2: IntConstant 4 [13]\n"
+ " 1: IntConstant 4 [13]\n"
+ " 2: IntConstant 5 [13]\n"
" 3: Goto\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 4: Equal(0, 0) [5]\n"
@@ -346,7 +346,7 @@
"BasicBlock 7, pred: 6\n"
" 12: Exit\n"
"BasicBlock 8, pred: 2, 3, succ: 4\n"
- " 13: Phi(2, 1) [8, 8, 11]\n"
+ " 13: Phi(1, 2) [8, 8, 11]\n"
" 14: Goto\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
@@ -496,7 +496,7 @@
// does not update the local.
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 0: IntConstant 0 [4, 4, 8, 8, 6, 6, 2, 2]\n"
+ " 0: IntConstant 0 [4, 8, 6, 6, 2, 2, 8, 4]\n"
" 1: Goto\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 2: Equal(0, 0) [3]\n"
diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali
index 733a1dd..3d06cdb 100644
--- a/test/510-checker-try-catch/smali/Builder.smali
+++ b/test/510-checker-try-catch/smali/Builder.smali
@@ -21,11 +21,11 @@
## CHECK-START: int Builder.testMultipleTryCatch(int, int, int) builder (after)
-## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry1:B\d+>>"
-## CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
-## CHECK-DAG: <<Minus2:i\d+>> IntConstant -2
-## CHECK-DAG: <<Minus3:i\d+>> IntConstant -3
+## CHECK: name "B0"
+## CHECK: successors "<<BEnterTry1:B\d+>>"
+## CHECK: <<Minus1:i\d+>> IntConstant -1
+## CHECK: <<Minus2:i\d+>> IntConstant -2
+## CHECK: <<Minus3:i\d+>> IntConstant -3
## CHECK: name "<<BTry1:B\d+>>"
## CHECK: predecessors "<<BEnterTry1>>"
@@ -236,10 +236,10 @@
## CHECK-START: int Builder.testMultipleExits(int, int) builder (after)
-## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
-## CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
-## CHECK-DAG: <<Minus2:i\d+>> IntConstant -2
+## CHECK: name "B0"
+## CHECK: successors "<<BEnterTry:B\d+>>"
+## CHECK: <<Minus1:i\d+>> IntConstant -1
+## CHECK: <<Minus2:i\d+>> IntConstant -2
## CHECK: name "<<BTry:B\d+>>"
## CHECK: predecessors "<<BEnterTry>>"
@@ -312,10 +312,10 @@
## CHECK-START: int Builder.testSharedBoundary(int, int, int) builder (after)
-## CHECK: name "B0"
-## CHECK: successors "<<BEnter1:B\d+>>"
-## CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
-## CHECK-DAG: <<Minus2:i\d+>> IntConstant -2
+## CHECK: name "B0"
+## CHECK: successors "<<BEnter1:B\d+>>"
+## CHECK: <<Minus1:i\d+>> IntConstant -1
+## CHECK: <<Minus2:i\d+>> IntConstant -2
## CHECK: name "<<BTry1:B\d+>>"
## CHECK: predecessors "<<BEnter1>>"
@@ -403,10 +403,10 @@
## CHECK-START: int Builder.testSharedBoundary_Reverse(int, int, int) builder (after)
-## CHECK: name "B0"
-## CHECK: successors "<<BGoto:B\d+>>"
-## CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
-## CHECK-DAG: <<Minus2:i\d+>> IntConstant -2
+## CHECK: name "B0"
+## CHECK: successors "<<BGoto:B\d+>>"
+## CHECK: <<Minus1:i\d+>> IntConstant -1
+## CHECK: <<Minus2:i\d+>> IntConstant -2
## CHECK: name "<<BGoto>>"
## CHECK: successors "<<BEnter2:B\d+>>"
@@ -504,9 +504,9 @@
## CHECK-START: int Builder.testNestedTry(int, int, int, int) builder (after)
-## CHECK: name "B0"
-## CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
-## CHECK-DAG: <<Minus2:i\d+>> IntConstant -2
+## CHECK: name "B0"
+## CHECK: <<Minus1:i\d+>> IntConstant -1
+## CHECK: <<Minus2:i\d+>> IntConstant -2
## CHECK: name "<<BTry1:B\d+>>"
## CHECK: predecessors "<<BEnter1:B\d+>>"