diff options
| -rw-r--r-- | build/Android.gtest.mk | 1 | ||||
| -rw-r--r-- | compiler/dex/local_value_numbering.cc | 401 | ||||
| -rw-r--r-- | compiler/dex/local_value_numbering.h | 116 | ||||
| -rw-r--r-- | compiler/dex/local_value_numbering_test.cc | 319 | ||||
| -rw-r--r-- | compiler/dex/mir_annotations.h | 4 | ||||
| -rw-r--r-- | compiler/dex/mir_graph.h | 2 |
6 files changed, 684 insertions, 159 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 791e95495b..3295d86d37 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -17,6 +17,7 @@ LOCAL_PATH := art TEST_COMMON_SRC_FILES := \ + compiler/dex/local_value_numbering_test.cc \ compiler/driver/compiler_driver_test.cc \ compiler/elf_writer_test.cc \ compiler/image_test.cc \ diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index 9e83210012..13846433b9 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -16,8 +16,121 @@ #include "local_value_numbering.h" +#include "mir_annotations.h" +#include "mir_graph.h" + namespace art { +uint16_t LocalValueNumbering::GetFieldId(const DexFile* dex_file, uint16_t field_idx) { + FieldReference key = { dex_file, field_idx }; + auto it = field_index_map_.find(key); + if (it != field_index_map_.end()) { + return it->second; + } + uint16_t id = field_index_map_.size(); + field_index_map_.Put(key, id); + return id; +} + +void LocalValueNumbering::AdvanceGlobalMemory() { + // See AdvanceMemoryVersion() for explanation. + global_memory_version_ = next_memory_version_; + ++next_memory_version_; +} + +uint16_t LocalValueNumbering::GetMemoryVersion(uint16_t base, uint16_t field, uint16_t type) { + // See AdvanceMemoryVersion() for explanation. + MemoryVersionKey key = { base, field, type }; + MemoryVersionMap::iterator it = memory_version_map_.find(key); + uint16_t memory_version = (it != memory_version_map_.end()) ? it->second : 0u; + if (base != NO_VALUE && non_aliasing_refs_.find(base) == non_aliasing_refs_.end()) { + // Check modifications by potentially aliased access. + MemoryVersionKey aliased_access_key = { NO_VALUE, field, type }; + auto aa_it = memory_version_map_.find(aliased_access_key); + if (aa_it != memory_version_map_.end() && aa_it->second > memory_version) { + memory_version = aa_it->second; + } + memory_version = std::max(memory_version, global_memory_version_); + } else if (base != NO_VALUE) { + // Ignore global_memory_version_ for access via unique references. + } else { + memory_version = std::max(memory_version, global_memory_version_); + } + return memory_version; +}; + +uint16_t LocalValueNumbering::AdvanceMemoryVersion(uint16_t base, uint16_t field, uint16_t type) { + // When we read the same value from memory, we want to assign the same value name to it. + // However, we need to be careful not to assign the same value name if the memory location + // may have been written to between the reads. To avoid that we do "memory versioning". + // + // For each write to a memory location (instance field, static field, array element) we assign + // a new memory version number to the location identified by the value name of the base register, + // the field id and type, or "{ base, field, type }". For static fields the "base" is NO_VALUE + // since they are not accessed via a reference. For arrays the "field" is NO_VALUE since they + // don't have a field id. + // + // To account for the possibility of aliased access to the same memory location via different + // "base", we also store the memory version number with the key "{ NO_VALUE, field, type }" + // if "base" is an aliasing reference and check it in GetMemoryVersion() on reads via + // aliasing references. A global memory version is set for method calls as a method can + // potentially write to any memory location accessed via an aliasing reference. + + uint16_t result = next_memory_version_; + ++next_memory_version_; + MemoryVersionKey key = { base, field, type }; + memory_version_map_.Overwrite(key, result); + if (base != NO_VALUE && non_aliasing_refs_.find(base) == non_aliasing_refs_.end()) { + // Advance memory version for aliased access. + MemoryVersionKey aliased_access_key = { NO_VALUE, field, type }; + memory_version_map_.Overwrite(aliased_access_key, result); + } + return result; +}; + +uint16_t LocalValueNumbering::MarkNonAliasingNonNull(MIR* mir) { + uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]); + SetOperandValue(mir->ssa_rep->defs[0], res); + DCHECK(null_checked_.find(res) == null_checked_.end()); + null_checked_.insert(res); + non_aliasing_refs_.insert(res); + return res; +} + +void LocalValueNumbering::MakeArgsAliasing(MIR* mir) { + for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) { + uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]); + non_aliasing_refs_.erase(reg); + } +} + +void LocalValueNumbering::HandleNullCheck(MIR* mir, uint16_t reg) { + if (null_checked_.find(reg) != null_checked_.end()) { + if (cu_->verbose) { + LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + null_checked_.insert(reg); + } +} + +void LocalValueNumbering::HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index) { + if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) { + if (cu_->verbose) { + LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; + } + // Use side effect to note range check completed. + (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); +} + +void LocalValueNumbering::HandlePutObject(MIR* mir) { + // If we're storing a non-aliasing reference, stop tracking it as non-aliasing now. + uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); + non_aliasing_refs_.erase(base); +} uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { uint16_t res = NO_VALUE; @@ -36,8 +149,6 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::CHECK_CAST: case Instruction::THROW: case Instruction::FILL_ARRAY_DATA: - case Instruction::FILLED_NEW_ARRAY: - case Instruction::FILLED_NEW_ARRAY_RANGE: case Instruction::PACKED_SWITCH: case Instruction::SPARSE_SWITCH: case Instruction::IF_EQ: @@ -52,8 +163,24 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::IF_GEZ: case Instruction::IF_GTZ: case Instruction::IF_LEZ: - case Instruction::INVOKE_STATIC_RANGE: - case Instruction::INVOKE_STATIC: + case kMirOpFusedCmplFloat: + case kMirOpFusedCmpgFloat: + case kMirOpFusedCmplDouble: + case kMirOpFusedCmpgDouble: + case kMirOpFusedCmpLong: + // Nothing defined - take no action. + break; + + case Instruction::FILLED_NEW_ARRAY: + case Instruction::FILLED_NEW_ARRAY_RANGE: + // Nothing defined but the result will be unique and non-null. + if (mir->next != nullptr && mir->next->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { + MarkNonAliasingNonNull(mir->next); + // The MOVE_RESULT_OBJECT will be processed next and we'll return the value name then. + } + MakeArgsAliasing(mir); + break; + case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: case Instruction::INVOKE_VIRTUAL: @@ -61,34 +188,38 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::INVOKE_SUPER: case Instruction::INVOKE_SUPER_RANGE: case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_INTERFACE_RANGE: - case kMirOpFusedCmplFloat: - case kMirOpFusedCmpgFloat: - case kMirOpFusedCmplDouble: - case kMirOpFusedCmpgDouble: - case kMirOpFusedCmpLong: - // Nothing defined - take no action. + case Instruction::INVOKE_INTERFACE_RANGE: { + // Nothing defined but handle the null check. + uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]); + HandleNullCheck(mir, reg); + } + // Intentional fall-through. + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_STATIC_RANGE: + AdvanceGlobalMemory(); + MakeArgsAliasing(mir); break; - case Instruction::MOVE_EXCEPTION: case Instruction::MOVE_RESULT: case Instruction::MOVE_RESULT_OBJECT: case Instruction::INSTANCE_OF: + // 1 result, treat as unique each time, use result s_reg - will be unique. + res = GetOperandValue(mir->ssa_rep->defs[0]); + SetOperandValue(mir->ssa_rep->defs[0], res); + break; + case Instruction::MOVE_EXCEPTION: case Instruction::NEW_INSTANCE: case Instruction::CONST_STRING: case Instruction::CONST_STRING_JUMBO: case Instruction::CONST_CLASS: - case Instruction::NEW_ARRAY: { - // 1 result, treat as unique each time, use result s_reg - will be unique. - uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]); - SetOperandValue(mir->ssa_rep->defs[0], res); - } + case Instruction::NEW_ARRAY: + // 1 result, treat as unique each time, use result s_reg - will be unique. + res = MarkNonAliasingNonNull(mir); break; - case Instruction::MOVE_RESULT_WIDE: { - // 1 wide result, treat as unique each time, use result s_reg - will be unique. - uint16_t res = GetOperandValueWide(mir->ssa_rep->defs[0]); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } + case Instruction::MOVE_RESULT_WIDE: + // 1 wide result, treat as unique each time, use result s_reg - will be unique. + res = GetOperandValueWide(mir->ssa_rep->defs[0]); + SetOperandValueWide(mir->ssa_rep->defs[0], res); break; case kMirOpPhi: @@ -104,35 +235,31 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::MOVE_OBJECT_16: case Instruction::MOVE_FROM16: case Instruction::MOVE_OBJECT_FROM16: - case kMirOpCopy: { - // Just copy value number of source to value number of resulit. - uint16_t res = GetOperandValue(mir->ssa_rep->uses[0]); - SetOperandValue(mir->ssa_rep->defs[0], res); - } + case kMirOpCopy: + // Just copy value number of source to value number of result. + res = GetOperandValue(mir->ssa_rep->uses[0]); + SetOperandValue(mir->ssa_rep->defs[0], res); break; case Instruction::MOVE_WIDE: case Instruction::MOVE_WIDE_16: - case Instruction::MOVE_WIDE_FROM16: { - // Just copy value number of source to value number of result. - uint16_t res = GetOperandValueWide(mir->ssa_rep->uses[0]); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } + case Instruction::MOVE_WIDE_FROM16: + // Just copy value number of source to value number of result. + res = GetOperandValueWide(mir->ssa_rep->uses[0]); + SetOperandValueWide(mir->ssa_rep->defs[0], res); break; case Instruction::CONST: case Instruction::CONST_4: - case Instruction::CONST_16: { - uint16_t res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB), - High16Bits(mir->dalvikInsn.vB >> 16), 0); - SetOperandValue(mir->ssa_rep->defs[0], res); - } + case Instruction::CONST_16: + res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB), + High16Bits(mir->dalvikInsn.vB >> 16), 0); + SetOperandValue(mir->ssa_rep->defs[0], res); break; - case Instruction::CONST_HIGH16: { - uint16_t res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0); - SetOperandValue(mir->ssa_rep->defs[0], res); - } + case Instruction::CONST_HIGH16: + res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0); + SetOperandValue(mir->ssa_rep->defs[0], res); break; case Instruction::CONST_WIDE_16: @@ -145,8 +272,8 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } else { high_res = LookupValue(Instruction::CONST, 0, 0, 2); } - uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3); - SetOperandValue(mir->ssa_rep->defs[0], res); + res = LookupValue(Instruction::CONST, low_res, high_res, 3); + SetOperandValueWide(mir->ssa_rep->defs[0], res); } break; @@ -157,7 +284,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { High16Bits(low_word), 1); uint16_t high_res = LookupValue(Instruction::CONST, Low16Bits(high_word), High16Bits(high_word), 2); - uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3); + res = LookupValue(Instruction::CONST, low_res, high_res, 3); SetOperandValueWide(mir->ssa_rep->defs[0], res); } break; @@ -165,7 +292,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::CONST_WIDE_HIGH16: { uint16_t low_res = LookupValue(Instruction::CONST, 0, 0, 1); uint16_t high_res = LookupValue(Instruction::CONST, 0, Low16Bits(mir->dalvikInsn.vB), 2); - uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3); + res = LookupValue(Instruction::CONST, low_res, high_res, 3); SetOperandValueWide(mir->ssa_rep->defs[0], res); } break; @@ -181,7 +308,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::FLOAT_TO_INT: { // res = op + 1 operand uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); SetOperandValue(mir->ssa_rep->defs[0], res); } break; @@ -192,7 +319,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::DOUBLE_TO_INT: { // res = op + 1 wide operand uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); SetOperandValue(mir->ssa_rep->defs[0], res); } break; @@ -205,7 +332,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::NEG_DOUBLE: { // wide res = op + 1 wide operand uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); SetOperandValueWide(mir->ssa_rep->defs[0], res); } break; @@ -216,7 +343,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::INT_TO_LONG: { // wide res = op + 1 operand uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); SetOperandValueWide(mir->ssa_rep->defs[0], res); } break; @@ -227,7 +354,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { // res = op + 2 wide operands uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); - uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + res = LookupValue(opcode, operand1, operand2, NO_VALUE); SetOperandValue(mir->ssa_rep->defs[0], res); } break; @@ -259,7 +386,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { // res = op + 2 operands uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); - uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + res = LookupValue(opcode, operand1, operand2, NO_VALUE); SetOperandValue(mir->ssa_rep->defs[0], res); } break; @@ -293,7 +420,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { // wide res = op + 2 wide operands uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); - uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + res = LookupValue(opcode, operand1, operand2, NO_VALUE); SetOperandValueWide(mir->ssa_rep->defs[0], res); } break; @@ -307,7 +434,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { // wide res = op + 1 wide operand + 1 operand uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); - uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + res = LookupValue(opcode, operand1, operand2, NO_VALUE); SetOperandValueWide(mir->ssa_rep->defs[0], res); } break; @@ -325,7 +452,7 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { // res = op + 2 operands uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); - uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + res = LookupValue(opcode, operand1, operand2, NO_VALUE); SetOperandValue(mir->ssa_rep->defs[0], res); } break; @@ -352,38 +479,25 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { // Same as res = op + 2 operands, except use vB as operand 2 uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); uint16_t operand2 = LookupValue(Instruction::CONST, mir->dalvikInsn.vB, 0, 0); - uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + res = LookupValue(opcode, operand1, operand2, NO_VALUE); SetOperandValue(mir->ssa_rep->defs[0], res); } break; - case Instruction::AGET_WIDE: - case Instruction::AGET: case Instruction::AGET_OBJECT: + case Instruction::AGET: + case Instruction::AGET_WIDE: case Instruction::AGET_BOOLEAN: case Instruction::AGET_BYTE: case Instruction::AGET_CHAR: case Instruction::AGET_SHORT: { + uint16_t type = opcode - Instruction::AGET; uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]); - if (null_checked_.find(array) != null_checked_.end()) { - if (cu_->verbose) { - LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; - } else { - null_checked_.insert(array); - } + HandleNullCheck(mir, array); uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]); - if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) { - if (cu_->verbose) { - LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; - } - // Use side effect to note range check completed. - (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); + HandleRangeCheck(mir, array, index); // Establish value number for loaded register. Note use of memory version. - uint16_t memory_version = GetMemoryVersion(array, NO_VALUE); + uint16_t memory_version = GetMemoryVersion(array, NO_VALUE, type); uint16_t res = LookupValue(ARRAY_REF, array, index, memory_version); if (opcode == Instruction::AGET_WIDE) { SetOperandValueWide(mir->ssa_rep->defs[0], res); @@ -393,116 +507,145 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } break; - case Instruction::APUT_WIDE: - case Instruction::APUT: case Instruction::APUT_OBJECT: - case Instruction::APUT_SHORT: - case Instruction::APUT_CHAR: + HandlePutObject(mir); + // Intentional fall-through. + case Instruction::APUT: + case Instruction::APUT_WIDE: case Instruction::APUT_BYTE: - case Instruction::APUT_BOOLEAN: { + case Instruction::APUT_BOOLEAN: + case Instruction::APUT_SHORT: + case Instruction::APUT_CHAR: { + uint16_t type = opcode - Instruction::APUT; int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1; int index_idx = array_idx + 1; uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]); - if (null_checked_.find(array) != null_checked_.end()) { - if (cu_->verbose) { - LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; - } else { - null_checked_.insert(array); - } + HandleNullCheck(mir, array); uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]); - if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) { - if (cu_->verbose) { - LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; - } - // Use side effect to note range check completed. - (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); + HandleRangeCheck(mir, array, index); // Rev the memory version - AdvanceMemoryVersion(array, NO_VALUE); + AdvanceMemoryVersion(array, NO_VALUE, type); } break; case Instruction::IGET_OBJECT: - case Instruction::IGET_WIDE: case Instruction::IGET: - case Instruction::IGET_CHAR: - case Instruction::IGET_SHORT: + case Instruction::IGET_WIDE: case Instruction::IGET_BOOLEAN: - case Instruction::IGET_BYTE: { + case Instruction::IGET_BYTE: + case Instruction::IGET_CHAR: + case Instruction::IGET_SHORT: { + uint16_t type = opcode - Instruction::IGET; uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); - if (null_checked_.find(base) != null_checked_.end()) { - if (cu_->verbose) { - LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + HandleNullCheck(mir, base); + const IFieldAnnotation& annotation = cu_->mir_graph->GetIFieldAnnotation(mir); + uint16_t memory_version; + uint16_t field_id; + if (annotation.IsVolatile()) { + // Volatile fields always get a new memory version; field id is irrelevant. + // Unresolved fields are always marked as volatile and handled the same way here. + field_id = 0u; + memory_version = next_memory_version_; + ++next_memory_version_; } else { - null_checked_.insert(base); + DCHECK(annotation.IsResolved()); + field_id = GetFieldId(annotation.DeclaringDexFile(), annotation.DeclaringFieldIndex()); + memory_version = std::max(unresolved_ifield_version_[type], + GetMemoryVersion(base, field_id, type)); } - uint16_t field_ref = mir->dalvikInsn.vC; - uint16_t memory_version = GetMemoryVersion(base, field_ref); if (opcode == Instruction::IGET_WIDE) { - uint16_t res = LookupValue(Instruction::IGET_WIDE, base, field_ref, memory_version); + res = LookupValue(Instruction::IGET_WIDE, base, field_id, memory_version); SetOperandValueWide(mir->ssa_rep->defs[0], res); } else { - uint16_t res = LookupValue(Instruction::IGET, base, field_ref, memory_version); + res = LookupValue(Instruction::IGET, base, field_id, memory_version); SetOperandValue(mir->ssa_rep->defs[0], res); } } break; - case Instruction::IPUT_WIDE: case Instruction::IPUT_OBJECT: + HandlePutObject(mir); + // Intentional fall-through. case Instruction::IPUT: + case Instruction::IPUT_WIDE: case Instruction::IPUT_BOOLEAN: case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { + uint16_t type = opcode - Instruction::IPUT; int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1; uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]); - if (null_checked_.find(base) != null_checked_.end()) { - if (cu_->verbose) { - LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + HandleNullCheck(mir, base); + const IFieldAnnotation& annotation = cu_->mir_graph->GetIFieldAnnotation(mir); + if (!annotation.IsResolved()) { + // Unresolved fields always alias with everything of the same type. + unresolved_ifield_version_[type] = next_memory_version_; + ++next_memory_version_; + } else if (annotation.IsVolatile()) { + // Nothing to do, resolved volatile fields always get a new memory version anyway and + // can't alias with resolved non-volatile fields. } else { - null_checked_.insert(base); + AdvanceMemoryVersion(base, GetFieldId(annotation.DeclaringDexFile(), + annotation.DeclaringFieldIndex()), type); } - uint16_t field_ref = mir->dalvikInsn.vC; - AdvanceMemoryVersion(base, field_ref); } break; case Instruction::SGET_OBJECT: case Instruction::SGET: + case Instruction::SGET_WIDE: case Instruction::SGET_BOOLEAN: case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: - case Instruction::SGET_SHORT: - case Instruction::SGET_WIDE: { - uint16_t field_ref = mir->dalvikInsn.vB; - uint16_t memory_version = GetMemoryVersion(NO_VALUE, field_ref); + case Instruction::SGET_SHORT: { + uint16_t type = opcode - Instruction::SGET; + const SFieldAnnotation& annotation = cu_->mir_graph->GetSFieldAnnotation(mir); + uint16_t memory_version; + uint16_t field_id; + if (annotation.IsVolatile()) { + // Volatile fields always get a new memory version; field id is irrelevant. + // Unresolved fields are always marked as volatile and handled the same way here. + field_id = 0u; + memory_version = next_memory_version_; + ++next_memory_version_; + } else { + DCHECK(annotation.IsResolved()); + field_id = GetFieldId(annotation.DeclaringDexFile(), annotation.DeclaringFieldIndex()); + memory_version = std::max(unresolved_sfield_version_[type], + GetMemoryVersion(NO_VALUE, field_id, type)); + } if (opcode == Instruction::SGET_WIDE) { - uint16_t res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_ref, memory_version); + res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_id, memory_version); SetOperandValueWide(mir->ssa_rep->defs[0], res); } else { - uint16_t res = LookupValue(Instruction::SGET, NO_VALUE, field_ref, memory_version); + res = LookupValue(Instruction::SGET, NO_VALUE, field_id, memory_version); SetOperandValue(mir->ssa_rep->defs[0], res); } } break; case Instruction::SPUT_OBJECT: + HandlePutObject(mir); + // Intentional fall-through. case Instruction::SPUT: + case Instruction::SPUT_WIDE: case Instruction::SPUT_BOOLEAN: case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: - case Instruction::SPUT_WIDE: { - uint16_t field_ref = mir->dalvikInsn.vB; - AdvanceMemoryVersion(NO_VALUE, field_ref); + case Instruction::SPUT_SHORT: { + uint16_t type = opcode - Instruction::SPUT; + const SFieldAnnotation& annotation = cu_->mir_graph->GetSFieldAnnotation(mir); + if (!annotation.IsResolved()) { + // Unresolved fields always alias with everything of the same type. + unresolved_sfield_version_[type] = next_memory_version_; + ++next_memory_version_; + } else if (annotation.IsVolatile()) { + // Nothing to do, resolved volatile fields always get a new memory version anyway and + // can't alias with resolved non-volatile fields. + } else { + AdvanceMemoryVersion(NO_VALUE, GetFieldId(annotation.DeclaringDexFile(), + annotation.DeclaringFieldIndex()), type); + } } break; } diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h index 33ca8f1ad8..348bedcc75 100644 --- a/compiler/dex/local_value_numbering.h +++ b/compiler/dex/local_value_numbering.h @@ -24,16 +24,78 @@ namespace art { -// Key is s_reg, value is value name. -typedef SafeMap<uint16_t, uint16_t> SregValueMap; -// Key is concatenation of quad, value is value name. -typedef SafeMap<uint64_t, uint16_t> ValueMap; -// Key represents a memory address, value is generation. -typedef SafeMap<uint32_t, uint16_t> MemoryVersionMap; +class DexFile; class LocalValueNumbering { + private: + // Field types correspond to the ordering of GET/PUT instructions; this order is the same + // for IGET, IPUT, SGET, SPUT, AGET and APUT: + // op 0 + // op_WIDE 1 + // op_OBJECT 2 + // op_BOOLEAN 3 + // op_BYTE 4 + // op_CHAR 5 + // op_SHORT 6 + static constexpr size_t kFieldTypeCount = 7; + + // FieldReference represents either a unique resolved field or all unresolved fields together. + struct FieldReference { + const DexFile* dex_file; + uint16_t field_idx; + }; + + struct FieldReferenceComparator { + bool operator()(const FieldReference& lhs, const FieldReference& rhs) const { + if (lhs.field_idx != rhs.field_idx) { + return lhs.field_idx < rhs.field_idx; + } + return lhs.dex_file < rhs.dex_file; + } + }; + + struct MemoryVersionKey { + uint16_t base; + uint16_t field_id; + uint16_t type; + }; + + struct MemoryVersionKeyComparator { + bool operator()(const MemoryVersionKey& lhs, const MemoryVersionKey& rhs) const { + if (lhs.base != rhs.base) { + return lhs.base < rhs.base; + } + if (lhs.field_id != rhs.field_id) { + return lhs.field_id < rhs.field_id; + } + return lhs.type < rhs.type; + } + }; + + // Key is s_reg, value is value name. + typedef SafeMap<uint16_t, uint16_t> SregValueMap; + // Key is concatenation of opcode, operand1, operand2 and modifier, value is value name. + typedef SafeMap<uint64_t, uint16_t> ValueMap; + // Key represents a memory address, value is generation. + typedef SafeMap<MemoryVersionKey, uint16_t, MemoryVersionKeyComparator> MemoryVersionMap; + // Maps field key to field id for resolved fields. + typedef SafeMap<FieldReference, uint32_t, FieldReferenceComparator> FieldIndexMap; + public: - explicit LocalValueNumbering(CompilationUnit* cu) : cu_(cu) {} + explicit LocalValueNumbering(CompilationUnit* cu) + : cu_(cu), + sreg_value_map_(), + sreg_wide_value_map_(), + value_map_(), + next_memory_version_(1u), + global_memory_version_(0u), + memory_version_map_(), + field_index_map_(), + non_aliasing_refs_(), + null_checked_() { + std::fill_n(unresolved_sfield_version_, kFieldTypeCount, 0u); + std::fill_n(unresolved_ifield_version_, kFieldTypeCount, 0u); + } static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) { return (static_cast<uint64_t>(op) << 48 | static_cast<uint64_t>(operand1) << 32 | @@ -59,29 +121,6 @@ class LocalValueNumbering { return (it != value_map_.end()); }; - uint16_t GetMemoryVersion(uint16_t base, uint16_t field) { - uint32_t key = (base << 16) | field; - uint16_t res; - MemoryVersionMap::iterator it = memory_version_map_.find(key); - if (it == memory_version_map_.end()) { - res = 0; - memory_version_map_.Put(key, res); - } else { - res = it->second; - } - return res; - }; - - void AdvanceMemoryVersion(uint16_t base, uint16_t field) { - uint32_t key = (base << 16) | field; - MemoryVersionMap::iterator it = memory_version_map_.find(key); - if (it == memory_version_map_.end()) { - memory_version_map_.Put(key, 0); - } else { - it->second++; - } - }; - void SetOperandValue(uint16_t s_reg, uint16_t value) { SregValueMap::iterator it = sreg_value_map_.find(s_reg); if (it != sreg_value_map_.end()) { @@ -129,11 +168,28 @@ class LocalValueNumbering { uint16_t GetValueNumber(MIR* mir); private: + uint16_t GetFieldId(const DexFile* dex_file, uint16_t field_idx); + void AdvanceGlobalMemory(); + uint16_t GetMemoryVersion(uint16_t base, uint16_t field, uint16_t type); + uint16_t AdvanceMemoryVersion(uint16_t base, uint16_t field, uint16_t type); + uint16_t MarkNonAliasingNonNull(MIR* mir); + void MakeArgsAliasing(MIR* mir); + void HandleNullCheck(MIR* mir, uint16_t reg); + void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index); + void HandlePutObject(MIR* mir); + CompilationUnit* const cu_; SregValueMap sreg_value_map_; SregValueMap sreg_wide_value_map_; ValueMap value_map_; + uint16_t next_memory_version_; + uint16_t global_memory_version_; + uint16_t unresolved_sfield_version_[kFieldTypeCount]; + uint16_t unresolved_ifield_version_[kFieldTypeCount]; MemoryVersionMap memory_version_map_; + FieldIndexMap field_index_map_; + // Value names of references to objects that cannot be reached through a different value name. + std::set<uint16_t> non_aliasing_refs_; std::set<uint16_t> null_checked_; }; diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc new file mode 100644 index 0000000000..230c012573 --- /dev/null +++ b/compiler/dex/local_value_numbering_test.cc @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vector> + +#include "local_value_numbering.h" +#include "compiler_internals.h" +#include "gtest/gtest.h" + +namespace art { + +class LocalValueNumberingTest : public testing::Test { + protected: + struct IFieldDef { + uint16_t field_idx; + uintptr_t declaring_dex_file; + uint16_t declaring_field_idx; + bool is_volatile; + }; + + struct SFieldDef { + uint16_t field_idx; + uintptr_t declaring_dex_file; + uint16_t declaring_field_idx; + bool is_volatile; + }; + + struct MIRDef { + static constexpr size_t kMaxSsaDefs = 2; + static constexpr size_t kMaxSsaUses = 3; + + Instruction::Code opcode; + int64_t value; + uint32_t field_annotation; + size_t num_uses; + int32_t uses[kMaxSsaUses]; + size_t num_defs; + int32_t defs[kMaxSsaDefs]; + }; + +#define DEF_CONST(opcode, reg, value) \ + { opcode, value, 0u, 0, { }, 1, { reg } } +#define DEF_CONST_WIDE(opcode, reg, value) \ + { opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } +#define DEF_IGET(opcode, reg, obj, field_annotation) \ + { opcode, 0u, field_annotation, 1, { obj }, 1, { reg } } +#define DEF_IGET_WIDE(opcode, reg, obj, field_annotation) \ + { opcode, 0u, field_annotation, 1, { obj }, 2, { reg, reg + 1 } } +#define DEF_IPUT(opcode, reg, obj, field_annotation) \ + { opcode, 0u, field_annotation, 2, { reg, obj }, 0, { } } +#define DEF_IPUT_WIDE(opcode, reg, obj, field_annotation) \ + { opcode, 0u, field_annotation, 3, { reg, reg + 1, obj }, 0, { } } +#define DEF_SGET(opcode, reg, field_annotation) \ + { opcode, 0u, field_annotation, 0, { }, 1, { reg } } +#define DEF_SGET_WIDE(opcode, reg, field_annotation) \ + { opcode, 0u, field_annotation, 0, { }, 2, { reg, reg + 1 } } +#define DEF_SPUT(opcode, reg, field_annotation) \ + { opcode, 0u, field_annotation, 1, { reg }, 0, { } } +#define DEF_SPUT_WIDE(opcode, reg, field_annotation) \ + { opcode, 0u, field_annotation, 2, { reg, reg + 1 }, 0, { } } +#define DEF_INVOKE1(opcode, reg) \ + { opcode, 0u, 0u, 1, { reg }, 0, { } } +#define DEF_UNIQUE_REF(opcode, reg) \ + { opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... + + void DoPrepareIFields(const IFieldDef* defs, size_t count) { + cu_.mir_graph->ifield_annotations_.Reset(); + cu_.mir_graph->ifield_annotations_.Resize(count); + for (size_t i = 0u; i != count; ++i) { + const IFieldDef* def = &defs[i]; + IFieldAnnotation annotation(def->field_idx); + if (def->declaring_dex_file != 0u) { + annotation.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); + annotation.declaring_field_idx_ = def->declaring_field_idx; + annotation.is_volatile_ = def->is_volatile ? 1u : 0u; + } + cu_.mir_graph->ifield_annotations_.Insert(annotation); + } + } + + template <size_t count> + void PrepareIFields(const IFieldDef (&defs)[count]) { + DoPrepareIFields(defs, count); + } + + void DoPrepareSFields(const SFieldDef* defs, size_t count) { + cu_.mir_graph->sfield_annotations_.Reset(); + cu_.mir_graph->sfield_annotations_.Resize(count); + for (size_t i = 0u; i != count; ++i) { + const SFieldDef* def = &defs[i]; + SFieldAnnotation annotation(def->field_idx); + if (def->declaring_dex_file != 0u) { + annotation.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); + annotation.declaring_field_idx_ = def->declaring_field_idx; + annotation.is_volatile_ = def->is_volatile ? 1u : 0u; + } + cu_.mir_graph->sfield_annotations_.Insert(annotation); + } + } + + template <size_t count> + void PrepareSFields(const SFieldDef (&defs)[count]) { + DoPrepareSFields(defs, count); + } + + void DoPrepareMIRs(const MIRDef* defs, size_t count) { + mir_count_ = count; + mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, ArenaAllocator::kAllocMIR)); + ssa_reps_.resize(count); + for (size_t i = 0u; i != count; ++i) { + const MIRDef* def = &defs[i]; + MIR* mir = &mirs_[i]; + mir->dalvikInsn.opcode = def->opcode; + mir->dalvikInsn.vB = static_cast<int32_t>(def->value); + mir->dalvikInsn.vB_wide = def->value; + if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) { + ASSERT_LT(def->field_annotation, cu_.mir_graph->ifield_annotations_.Size()); + mir->meta.ifield_annotation = def->field_annotation; + } else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) { + ASSERT_LT(def->field_annotation, cu_.mir_graph->sfield_annotations_.Size()); + mir->meta.sfield_annotation = def->field_annotation; + } + mir->ssa_rep = &ssa_reps_[i]; + mir->ssa_rep->num_uses = def->num_uses; + mir->ssa_rep->uses = const_cast<int32_t*>(def->uses); // Not modified by LVN. + mir->ssa_rep->fp_use = nullptr; // Not used by LVN. + mir->ssa_rep->num_defs = def->num_defs; + mir->ssa_rep->defs = const_cast<int32_t*>(def->defs); // Not modified by LVN. + mir->ssa_rep->fp_def = nullptr; // Not used by LVN. + mir->dalvikInsn.opcode = def->opcode; + mir->offset = i; // LVN uses offset only for debug output + mir->width = 1u; // Not used by LVN. + mir->optimization_flags = 0u; + + if (i != 0u) { + mirs_[i - 1u].next = mir; + } + } + mirs_[count - 1u].next = nullptr; + } + + template <size_t count> + void PrepareMIRs(const MIRDef (&defs)[count]) { + DoPrepareMIRs(defs, count); + } + + void PerformLVN() { + value_names_.resize(mir_count_); + for (size_t i = 0; i != mir_count_; ++i) { + value_names_[i] = lvn_.GetValueNumber(&mirs_[i]); + } + } + + LocalValueNumberingTest() : pool_(), cu_(&pool_), mir_count_(0u), mirs_(nullptr), lvn_(&cu_) { + cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); + } + + ArenaPool pool_; + CompilationUnit cu_; + size_t mir_count_; + MIR* mirs_; + std::vector<SSARepresentation> ssa_reps_; + std::vector<uint16_t> value_names_; + LocalValueNumbering lvn_; +}; + +TEST_F(LocalValueNumberingTest, TestIGetIGetInvokeIGet) { + static const IFieldDef ifields[] = { + { 1u, 1u, 1u, false } + }; + static const MIRDef mirs[] = { + DEF_IGET(Instruction::IGET, 0u, 10u, 0u), + DEF_IGET(Instruction::IGET, 1u, 10u, 0u), + DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u), + DEF_IGET(Instruction::IGET, 2u, 10u, 0u), + }; + + PrepareIFields(ifields); + PrepareMIRs(mirs); + PerformLVN(); + ASSERT_EQ(value_names_.size(), 4u); + EXPECT_EQ(value_names_[0], value_names_[1]); + EXPECT_NE(value_names_[0], value_names_[3]); + EXPECT_EQ(mirs_[0].optimization_flags, 0u); + EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK); + EXPECT_EQ(mirs_[2].optimization_flags, 0u); + EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); +} + +TEST_F(LocalValueNumberingTest, TestIGetIPutIGetIGetIGet) { + static const IFieldDef ifields[] = { + { 1u, 1u, 1u, false }, + { 2u, 1u, 2u, false }, + }; + static const MIRDef mirs[] = { + DEF_IGET(Instruction::IGET, 0u, 10u, 0u), + DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // May alias. + DEF_IGET(Instruction::IGET, 2u, 10u, 0u), + DEF_IGET(Instruction::IGET, 3u, 0u, 1u), + DEF_IGET(Instruction::IGET, 4u, 2u, 1u), + }; + + PrepareIFields(ifields); + PrepareMIRs(mirs); + PerformLVN(); + ASSERT_EQ(value_names_.size(), 5u); + EXPECT_NE(value_names_[0], value_names_[2]); + EXPECT_NE(value_names_[3], value_names_[4]); + EXPECT_EQ(mirs_[0].optimization_flags, 0u); + EXPECT_EQ(mirs_[1].optimization_flags, 0u); + EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK); + EXPECT_EQ(mirs_[3].optimization_flags, 0u); + EXPECT_EQ(mirs_[4].optimization_flags, 0u); +} + +TEST_F(LocalValueNumberingTest, TestUniquePreserve1) { + static const IFieldDef ifields[] = { + { 1u, 1u, 1u, false }, + }; + static const MIRDef mirs[] = { + DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), + DEF_IGET(Instruction::IGET, 0u, 10u, 0u), + DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 10u is unique. + DEF_IGET(Instruction::IGET, 2u, 10u, 0u), + }; + + PrepareIFields(ifields); + PrepareMIRs(mirs); + PerformLVN(); + ASSERT_EQ(value_names_.size(), 4u); + EXPECT_EQ(value_names_[1], value_names_[3]); + EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK); + EXPECT_EQ(mirs_[2].optimization_flags, 0u); + EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); +} + +TEST_F(LocalValueNumberingTest, TestUniquePreserve2) { + static const IFieldDef ifields[] = { + { 1u, 1u, 1u, false }, + }; + static const MIRDef mirs[] = { + DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 11u), + DEF_IGET(Instruction::IGET, 0u, 10u, 0u), + DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 11u is unique. + DEF_IGET(Instruction::IGET, 2u, 10u, 0u), + }; + + PrepareIFields(ifields); + PrepareMIRs(mirs); + PerformLVN(); + ASSERT_EQ(value_names_.size(), 4u); + EXPECT_EQ(value_names_[1], value_names_[3]); + EXPECT_EQ(mirs_[1].optimization_flags, 0u); + EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK); + EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); +} + +TEST_F(LocalValueNumberingTest, TestUniquePreserveAndEscape) { + static const IFieldDef ifields[] = { + { 1u, 1u, 1u, false }, + }; + static const MIRDef mirs[] = { + DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), + DEF_IGET(Instruction::IGET, 0u, 10u, 0u), + DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u), // 10u still unique. + DEF_IGET(Instruction::IGET, 2u, 10u, 0u), + DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 10u), // 10u not unique anymore. + DEF_IGET(Instruction::IGET, 3u, 10u, 0u), + }; + + PrepareIFields(ifields); + PrepareMIRs(mirs); + PerformLVN(); + ASSERT_EQ(value_names_.size(), 6u); + EXPECT_EQ(value_names_[1], value_names_[3]); + EXPECT_NE(value_names_[1], value_names_[5]); + EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK); + EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); + EXPECT_EQ(mirs_[5].optimization_flags, MIR_IGNORE_NULL_CHECK); +} + +TEST_F(LocalValueNumberingTest, TestVolatile) { + static const IFieldDef ifields[] = { + { 1u, 1u, 1u, false }, + { 2u, 1u, 2u, true }, + }; + static const MIRDef mirs[] = { + DEF_IGET(Instruction::IGET, 0u, 10u, 1u), // Volatile. + DEF_IGET(Instruction::IGET, 1u, 0u, 0u), // Non-volatile. + DEF_IGET(Instruction::IGET, 2u, 10u, 1u), // Volatile. + DEF_IGET(Instruction::IGET, 3u, 2u, 1u), // Non-volatile. + }; + + PrepareIFields(ifields); + PrepareMIRs(mirs); + PerformLVN(); + ASSERT_EQ(value_names_.size(), 4u); + EXPECT_NE(value_names_[0], value_names_[2]); // Volatile has always different value name. + EXPECT_NE(value_names_[1], value_names_[3]); // Used different base because of volatile. + EXPECT_EQ(mirs_[0].optimization_flags, 0u); + EXPECT_EQ(mirs_[1].optimization_flags, 0u); + EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK); + EXPECT_EQ(mirs_[3].optimization_flags, 0u); +} + +} // namespace art diff --git a/compiler/dex/mir_annotations.h b/compiler/dex/mir_annotations.h index 85761de590..cb7b589fa0 100644 --- a/compiler/dex/mir_annotations.h +++ b/compiler/dex/mir_annotations.h @@ -108,6 +108,8 @@ class IFieldAnnotation { uint16_t declaring_class_idx_; // The field index in the dex file that defines field, 0 if unresolved. uint16_t declaring_field_idx_; + + friend class LocalValueNumberingTest; }; class SFieldAnnotation { @@ -208,6 +210,8 @@ class SFieldAnnotation { uint16_t declaring_class_idx_; // The field index in the dex file that defines field, 0 if unresolved. uint16_t declaring_field_idx_; + + friend class LocalValueNumberingTest; }; } // namespace art diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index ea0289b02b..df48f7ef01 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -939,6 +939,8 @@ class MIRGraph { size_t max_available_special_compiler_temps_; GrowableArray<IFieldAnnotation> ifield_annotations_; GrowableArray<SFieldAnnotation> sfield_annotations_; + + friend class LocalValueNumberingTest; }; } // namespace art |