diff options
44 files changed, 2335 insertions, 349 deletions
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index f1d11354fa..cc40522731 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -39,10 +39,10 @@ using namespace vixl32; // NOLINT(build/namespaces) using helpers::DRegisterFrom; using helpers::DWARFReg; -using helpers::FromLowSToD; using helpers::HighDRegisterFrom; using helpers::HighRegisterFrom; using helpers::InputOperandAt; +using helpers::InputRegister; using helpers::InputRegisterAt; using helpers::InputSRegisterAt; using helpers::InputVRegisterAt; @@ -340,6 +340,46 @@ class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL { DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL); }; +class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL { + public: + explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction) + : SlowPathCodeARMVIXL(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); + LocationSummary* locations = instruction_->GetLocations(); + + __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } + // We're moving two locations to locations that could overlap, so we need a parallel + // move resolver. + InvokeRuntimeCallingConventionARMVIXL calling_convention; + codegen->EmitParallelMoves( + locations->InAt(0), + LocationFrom(calling_convention.GetRegisterAt(0)), + Primitive::kPrimInt, + locations->InAt(1), + LocationFrom(calling_convention.GetRegisterAt(1)), + Primitive::kPrimInt); + QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt() + ? kQuickThrowStringBounds + : kQuickThrowArrayBounds; + arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); + CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); + } + + bool IsFatal() const OVERRIDE { return true; } + + const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; } + + private: + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL); +}; + class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { public: LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit) @@ -394,6 +434,66 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL); }; +class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL { + public: + explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction) + : SlowPathCodeARMVIXL(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); + __ Bind(GetEntryLabel()); + arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickDeoptimize, void, void>(); + } + + const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; } + + private: + DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL); +}; + +class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL { + public: + explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); + + InvokeRuntimeCallingConventionARMVIXL calling_convention; + HParallelMove parallel_move(codegen->GetGraph()->GetArena()); + parallel_move.AddMove( + locations->InAt(0), + LocationFrom(calling_convention.GetRegisterAt(0)), + Primitive::kPrimNot, + nullptr); + parallel_move.AddMove( + locations->InAt(1), + LocationFrom(calling_convention.GetRegisterAt(1)), + Primitive::kPrimInt, + nullptr); + parallel_move.AddMove( + locations->InAt(2), + LocationFrom(calling_convention.GetRegisterAt(2)), + Primitive::kPrimNot, + nullptr); + codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); + + CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); + arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>(); + RestoreLiveRegisters(codegen, locations); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; } + + private: + DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL); +}; + + inline vixl32::Condition ARMCondition(IfCondition cond) { switch (cond) { case kCondEQ: return eq; @@ -795,14 +895,14 @@ void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) { __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0); } else { DCHECK_EQ(type, Primitive::kPrimDouble); - __ Vcmp(F64, FromLowSToD(LowSRegisterFrom(lhs_loc)), 0.0); + __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0); } } else { if (type == Primitive::kPrimFloat) { __ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1)); } else { DCHECK_EQ(type, Primitive::kPrimDouble); - __ Vcmp(FromLowSToD(LowSRegisterFrom(lhs_loc)), FromLowSToD(LowSRegisterFrom(rhs_loc))); + __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc)); } } } @@ -1028,6 +1128,24 @@ void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) { GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target); } +void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) { + LocationSummary* locations = new (GetGraph()->GetArena()) + LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); + locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. + if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) { + locations->SetInAt(0, Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) { + SlowPathCodeARMVIXL* slow_path = + deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize); + GenerateTestAndBranch(deoptimize, + /* condition_input_index */ 0, + slow_path->GetEntryLabel(), + /* false_target */ nullptr); +} + void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); if (Primitive::IsFloatingPointType(select->GetType())) { @@ -1250,7 +1368,8 @@ void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { +void InstructionCodeGeneratorARMVIXL::VisitFloatConstant( + HFloatConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. } @@ -1260,7 +1379,8 @@ void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(HDoubleConstant* constant ATTRIBUTE_UNUSED) { +void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant( + HDoubleConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. } @@ -1693,7 +1813,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimDouble: { // Processing a Dex `double-to-int' instruction. vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0)); - __ Vcvt(I32, F64, temp_s, FromLowSToD(LowSRegisterFrom(in))); + __ Vcvt(I32, F64, temp_s, DRegisterFrom(in)); __ Vmov(OutputRegister(conversion), temp_s); break; } @@ -1781,7 +1901,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimDouble: // Processing a Dex `double-to-float' instruction. - __ Vcvt(F32, F64, OutputSRegister(conversion), FromLowSToD(LowSRegisterFrom(in))); + __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in)); break; default: @@ -1800,7 +1920,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimChar: { // Processing a Dex `int-to-double' instruction. __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0)); - __ Vcvt(F64, I32, FromLowSToD(LowSRegisterFrom(out)), LowSRegisterFrom(out)); + __ Vcvt(F64, I32, DRegisterFrom(out), LowSRegisterFrom(out)); break; } @@ -1810,13 +1930,12 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve vixl32::Register high = HighRegisterFrom(in); vixl32::SRegister out_s = LowSRegisterFrom(out); - vixl32::DRegister out_d = FromLowSToD(out_s); + vixl32::DRegister out_d = DRegisterFrom(out); vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0)); - vixl32::DRegister temp_d = FromLowSToD(temp_s); + vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0)); - vixl32::SRegister constant_s = LowSRegisterFrom(locations->GetTemp(1)); - vixl32::DRegister constant_d = FromLowSToD(constant_s); + vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(0)); // temp_d = int-to-double(high) __ Vmov(temp_s, high); @@ -1833,7 +1952,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimFloat: // Processing a Dex `float-to-double' instruction. - __ Vcvt(F64, F32, FromLowSToD(LowSRegisterFrom(out)), InputSRegisterAt(conversion, 0)); + __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0)); break; default: @@ -2782,6 +2901,17 @@ void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) { } } +void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) { + __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1); +} + void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); @@ -3053,7 +3183,7 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction, } case Primitive::kPrimDouble: { - vixl32::DRegister value_reg = FromLowSToD(LowSRegisterFrom(value)); + vixl32::DRegister value_reg = DRegisterFrom(value); if (is_volatile && !atomic_ldrd_strd) { vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0)); vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1)); @@ -3281,7 +3411,7 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction, break; case Primitive::kPrimDouble: { - vixl32::DRegister out_dreg = FromLowSToD(LowSRegisterFrom(out)); + vixl32::DRegister out_dreg = DRegisterFrom(out); if (is_volatile && !atomic_ldrd_strd) { vixl32::Register lo = RegisterFrom(locations->GetTemp(0)); vixl32::Register hi = RegisterFrom(locations->GetTemp(1)); @@ -3345,6 +3475,14 @@ void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instr HandleFieldGet(instruction, instruction->GetFieldInfo()); } +void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) { + HandleFieldSet(instruction, instruction->GetFieldInfo()); +} + +void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) { + HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); +} + void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) { // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() @@ -3381,6 +3519,554 @@ void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) { codegen_->GenerateNullCheck(instruction); } +static LoadOperandType GetLoadOperandType(Primitive::Type type) { + switch (type) { + case Primitive::kPrimNot: + return kLoadWord; + case Primitive::kPrimBoolean: + return kLoadUnsignedByte; + case Primitive::kPrimByte: + return kLoadSignedByte; + case Primitive::kPrimChar: + return kLoadUnsignedHalfword; + case Primitive::kPrimShort: + return kLoadSignedHalfword; + case Primitive::kPrimInt: + return kLoadWord; + case Primitive::kPrimLong: + return kLoadWordPair; + case Primitive::kPrimFloat: + return kLoadSWord; + case Primitive::kPrimDouble: + return kLoadDWord; + default: + LOG(FATAL) << "Unreachable type " << type; + UNREACHABLE(); + } +} + +static StoreOperandType GetStoreOperandType(Primitive::Type type) { + switch (type) { + case Primitive::kPrimNot: + return kStoreWord; + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + return kStoreByte; + case Primitive::kPrimChar: + case Primitive::kPrimShort: + return kStoreHalfword; + case Primitive::kPrimInt: + return kStoreWord; + case Primitive::kPrimLong: + return kStoreWordPair; + case Primitive::kPrimFloat: + return kStoreSWord; + case Primitive::kPrimDouble: + return kStoreDWord; + default: + LOG(FATAL) << "Unreachable type " << type; + UNREACHABLE(); + } +} + +void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(Primitive::Type type, + Location out_loc, + vixl32::Register base, + vixl32::Register reg_index, + vixl32::Condition cond) { + uint32_t shift_count = Primitive::ComponentSizeShift(type); + MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count); + + switch (type) { + case Primitive::kPrimByte: + __ Ldrsb(cond, RegisterFrom(out_loc), mem_address); + break; + case Primitive::kPrimBoolean: + __ Ldrb(cond, RegisterFrom(out_loc), mem_address); + break; + case Primitive::kPrimShort: + __ Ldrsh(cond, RegisterFrom(out_loc), mem_address); + break; + case Primitive::kPrimChar: + __ Ldrh(cond, RegisterFrom(out_loc), mem_address); + break; + case Primitive::kPrimNot: + case Primitive::kPrimInt: + __ Ldr(cond, RegisterFrom(out_loc), mem_address); + break; + // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types. + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + default: + LOG(FATAL) << "Unreachable type " << type; + UNREACHABLE(); + } +} + +void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(Primitive::Type type, + Location loc, + vixl32::Register base, + vixl32::Register reg_index, + vixl32::Condition cond) { + uint32_t shift_count = Primitive::ComponentSizeShift(type); + MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count); + + switch (type) { + case Primitive::kPrimByte: + case Primitive::kPrimBoolean: + __ Strb(cond, RegisterFrom(loc), mem_address); + break; + case Primitive::kPrimShort: + case Primitive::kPrimChar: + __ Strh(cond, RegisterFrom(loc), mem_address); + break; + case Primitive::kPrimNot: + case Primitive::kPrimInt: + __ Str(cond, RegisterFrom(loc), mem_address); + break; + // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types. + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + default: + LOG(FATAL) << "Unreachable type " << type; + UNREACHABLE(); + } +} + +void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { + bool object_array_get_with_read_barrier = + kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, + object_array_get_with_read_barrier ? + LocationSummary::kCallOnSlowPath : + LocationSummary::kNoCall); + if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { + TODO_VIXL32(FATAL); + } + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (Primitive::IsFloatingPointType(instruction->GetType())) { + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + } else { + // The output overlaps in the case of an object array get with + // read barriers enabled: we do not want the move to overwrite the + // array's location, as we need it to emit the read barrier. + locations->SetOut( + Location::RequiresRegister(), + object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); + } + // We need a temporary register for the read barrier marking slow + // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. + // Also need for String compression feature. + if ((object_array_get_with_read_barrier && kUseBakerReadBarrier) + || (mirror::kUseStringCompression && instruction->IsStringCharAt())) { + TODO_VIXL32(FATAL); + } +} + +void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { + UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + LocationSummary* locations = instruction->GetLocations(); + Location obj_loc = locations->InAt(0); + vixl32::Register obj = InputRegisterAt(instruction, 0); + Location index = locations->InAt(1); + Location out_loc = locations->Out(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); + Primitive::Type type = instruction->GetType(); + const bool maybe_compressed_char_at = mirror::kUseStringCompression && + instruction->IsStringCharAt(); + HInstruction* array_instr = instruction->GetArray(); + bool has_intermediate_address = array_instr->IsIntermediateAddress(); + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); + + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: + case Primitive::kPrimInt: { + if (index.IsConstant()) { + int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue(); + if (maybe_compressed_char_at) { + TODO_VIXL32(FATAL); + } else { + uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type)); + + LoadOperandType load_type = GetLoadOperandType(type); + GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset); + } + } else { + vixl32::Register temp = temps.Acquire(); + + if (has_intermediate_address) { + TODO_VIXL32(FATAL); + } else { + __ Add(temp, obj, data_offset); + } + if (maybe_compressed_char_at) { + TODO_VIXL32(FATAL); + } else { + codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index)); + } + } + break; + } + + case Primitive::kPrimNot: { + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); + // /* HeapReference<Object> */ out = + // *(obj + data_offset + index * sizeof(HeapReference<Object>)) + if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { + TODO_VIXL32(FATAL); + } else { + vixl32::Register out = OutputRegister(instruction); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); + // If read barriers are enabled, emit read barriers other than + // Baker's using a slow path (and also unpoison the loaded + // reference, if heap poisoning is enabled). + codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset); + } else { + vixl32::Register temp = temps.Acquire(); + + if (has_intermediate_address) { + TODO_VIXL32(FATAL); + } else { + __ Add(temp, obj, data_offset); + } + codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index)); + + codegen_->MaybeRecordImplicitNullCheck(instruction); + // If read barriers are enabled, emit read barriers other than + // Baker's using a slow path (and also unpoison the loaded + // reference, if heap poisoning is enabled). + codegen_->MaybeGenerateReadBarrierSlow( + instruction, out_loc, out_loc, obj_loc, data_offset, index); + } + } + break; + } + + case Primitive::kPrimLong: { + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset); + } else { + vixl32::Register temp = temps.Acquire(); + __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); + GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset); + } + break; + } + + case Primitive::kPrimFloat: { + vixl32::SRegister out = SRegisterFrom(out_loc); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + GetAssembler()->LoadSFromOffset(out, obj, offset); + } else { + vixl32::Register temp = temps.Acquire(); + __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4)); + GetAssembler()->LoadSFromOffset(out, temp, data_offset); + } + break; + } + + case Primitive::kPrimDouble: { + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset); + } else { + vixl32::Register temp = temps.Acquire(); + __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); + GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset); + } + break; + } + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << type; + UNREACHABLE(); + } + + if (type == Primitive::kPrimNot) { + // Potential implicit null checks, in the case of reference + // arrays, are handled in the previous switch statement. + } else if (!maybe_compressed_char_at) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } +} + +void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) { + Primitive::Type value_type = instruction->GetComponentType(); + + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); + bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); + + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( + instruction, + may_need_runtime_call_for_type_check ? + LocationSummary::kCallOnSlowPath : + LocationSummary::kNoCall); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (Primitive::IsFloatingPointType(value_type)) { + locations->SetInAt(2, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(2, Location::RequiresRegister()); + } + if (needs_write_barrier) { + // Temporary registers for the write barrier. + locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. + locations->AddTemp(Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { + UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + LocationSummary* locations = instruction->GetLocations(); + vixl32::Register array = InputRegisterAt(instruction, 0); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->GetComponentType(); + bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); + uint32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value(); + Location value_loc = locations->InAt(2); + HInstruction* array_instr = instruction->GetArray(); + bool has_intermediate_address = array_instr->IsIntermediateAddress(); + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: + case Primitive::kPrimInt: { + if (index.IsConstant()) { + int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue(); + uint32_t full_offset = + data_offset + (const_index << Primitive::ComponentSizeShift(value_type)); + StoreOperandType store_type = GetStoreOperandType(value_type); + GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset); + } else { + vixl32::Register temp = temps.Acquire(); + + if (has_intermediate_address) { + TODO_VIXL32(FATAL); + } else { + __ Add(temp, array, data_offset); + } + codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); + } + break; + } + + case Primitive::kPrimNot: { + vixl32::Register value = RegisterFrom(value_loc); + // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet. + // See the comment in instruction_simplifier_shared.cc. + DCHECK(!has_intermediate_address); + + if (instruction->InputAt(2)->IsNullConstant()) { + // Just setting null. + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + GetAssembler()->StoreToOffset(kStoreWord, value, array, offset); + } else { + DCHECK(index.IsRegister()) << index; + vixl32::Register temp = temps.Acquire(); + __ Add(temp, array, data_offset); + codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); + } + codegen_->MaybeRecordImplicitNullCheck(instruction); + DCHECK(!needs_write_barrier); + DCHECK(!may_need_runtime_call_for_type_check); + break; + } + + DCHECK(needs_write_barrier); + Location temp1_loc = locations->GetTemp(0); + vixl32::Register temp1 = RegisterFrom(temp1_loc); + Location temp2_loc = locations->GetTemp(1); + vixl32::Register temp2 = RegisterFrom(temp2_loc); + uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); + uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); + vixl32::Label done; + SlowPathCodeARMVIXL* slow_path = nullptr; + + if (may_need_runtime_call_for_type_check) { + slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction); + codegen_->AddSlowPath(slow_path); + if (instruction->GetValueCanBeNull()) { + vixl32::Label non_zero; + __ Cbnz(value, &non_zero); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + GetAssembler()->StoreToOffset(kStoreWord, value, array, offset); + } else { + DCHECK(index.IsRegister()) << index; + vixl32::Register temp = temps.Acquire(); + __ Add(temp, array, data_offset); + codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); + } + codegen_->MaybeRecordImplicitNullCheck(instruction); + __ B(&done); + __ Bind(&non_zero); + } + + // Note that when read barriers are enabled, the type checks + // are performed without read barriers. This is fine, even in + // the case where a class object is in the from-space after + // the flip, as a comparison involving such a type would not + // produce a false positive; it may of course produce a false + // negative, in which case we would take the ArraySet slow + // path. + + // /* HeapReference<Class> */ temp1 = array->klass_ + GetAssembler()->LoadFromOffset(kLoadWord, temp1, array, class_offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); + GetAssembler()->MaybeUnpoisonHeapReference(temp1); + + // /* HeapReference<Class> */ temp1 = temp1->component_type_ + GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset); + // /* HeapReference<Class> */ temp2 = value->klass_ + GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset); + // If heap poisoning is enabled, no need to unpoison `temp1` + // nor `temp2`, as we are comparing two poisoned references. + __ Cmp(temp1, temp2); + + if (instruction->StaticTypeOfArrayIsObjectArray()) { + vixl32::Label do_put; + __ B(eq, &do_put); + // If heap poisoning is enabled, the `temp1` reference has + // not been unpoisoned yet; unpoison it now. + GetAssembler()->MaybeUnpoisonHeapReference(temp1); + + // /* HeapReference<Class> */ temp1 = temp1->super_class_ + GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset); + // If heap poisoning is enabled, no need to unpoison + // `temp1`, as we are comparing against null below. + __ Cbnz(temp1, slow_path->GetEntryLabel()); + __ Bind(&do_put); + } else { + __ B(ne, slow_path->GetEntryLabel()); + } + } + + vixl32::Register source = value; + if (kPoisonHeapReferences) { + // Note that in the case where `value` is a null reference, + // we do not enter this block, as a null reference does not + // need poisoning. + DCHECK_EQ(value_type, Primitive::kPrimNot); + __ Mov(temp1, value); + GetAssembler()->PoisonHeapReference(temp1); + source = temp1; + } + + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + GetAssembler()->StoreToOffset(kStoreWord, source, array, offset); + } else { + DCHECK(index.IsRegister()) << index; + + vixl32::Register temp = temps.Acquire(); + __ Add(temp, array, data_offset); + codegen_->StoreToShiftedRegOffset(value_type, + LocationFrom(source), + temp, + RegisterFrom(index)); + } + + if (!may_need_runtime_call_for_type_check) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } + + codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); + + if (done.IsReferenced()) { + __ Bind(&done); + } + + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } + + break; + } + + case Primitive::kPrimLong: { + Location value = locations->InAt(2); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset); + } else { + vixl32::Register temp = temps.Acquire(); + __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); + GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset); + } + break; + } + + case Primitive::kPrimFloat: { + Location value = locations->InAt(2); + DCHECK(value.IsFpuRegister()); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset); + } else { + vixl32::Register temp = temps.Acquire(); + __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4)); + GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset); + } + break; + } + + case Primitive::kPrimDouble: { + Location value = locations->InAt(2); + DCHECK(value.IsFpuRegisterPair()); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset); + } else { + vixl32::Register temp = temps.Acquire(); + __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); + GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset); + } + break; + } + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << value_type; + UNREACHABLE(); + } + + // Objects are handled in the switch. + if (value_type != Primitive::kPrimNot) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } +} + void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -3397,6 +4083,28 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction // TODO(VIXL): https://android-review.googlesource.com/#/c/272625/ } +void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) { + RegisterSet caller_saves = RegisterSet::Empty(); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0))); + caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1))); + LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) { + SlowPathCodeARMVIXL* slow_path = + new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction); + codegen_->AddSlowPath(slow_path); + + vixl32::Register index = InputRegisterAt(instruction, 0); + vixl32::Register length = InputRegisterAt(instruction, 1); + + __ Cmp(index, length); + __ B(hs, slow_path->GetEntryLabel()); +} + void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp, vixl32::Register card, vixl32::Register object, @@ -3509,7 +4217,7 @@ void ParallelMoveResolverARMVIXL::EmitMove(size_t index) { } } else if (source.IsFpuRegister()) { if (destination.IsRegister()) { - TODO_VIXL32(FATAL); + __ Vmov(RegisterFrom(destination), SRegisterFrom(source)); } else if (destination.IsFpuRegister()) { __ Vmov(SRegisterFrom(destination), SRegisterFrom(source)); } else { @@ -3534,9 +4242,7 @@ void ParallelMoveResolverARMVIXL::EmitMove(size_t index) { __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source)); __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source)); } else if (destination.IsFpuRegisterPair()) { - __ Vmov(FromLowSToD(LowSRegisterFrom(destination)), - LowRegisterFrom(source), - HighRegisterFrom(source)); + __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source)); } else { DCHECK(destination.IsDoubleStackSlot()) << destination; DCHECK(ExpectedPairLayout(source)); @@ -3547,7 +4253,7 @@ void ParallelMoveResolverARMVIXL::EmitMove(size_t index) { } } else if (source.IsFpuRegisterPair()) { if (destination.IsRegisterPair()) { - TODO_VIXL32(FATAL); + __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source)); } else if (destination.IsFpuRegisterPair()) { __ Vmov(DRegisterFrom(destination), DRegisterFrom(source)); } else { @@ -3586,7 +4292,7 @@ void ParallelMoveResolverARMVIXL::EmitMove(size_t index) { } else if (constant->IsDoubleConstant()) { double value = constant->AsDoubleConstant()->GetValue(); if (destination.IsFpuRegisterPair()) { - __ Vmov(FromLowSToD(LowSRegisterFrom(destination)), value); + __ Vmov(DRegisterFrom(destination), value); } else { DCHECK(destination.IsDoubleStackSlot()) << destination; uint64_t int_value = bit_cast<uint64_t, double>(value); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 02bf960e18..df7d46782d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -106,13 +106,18 @@ class LoadClassSlowPathARMVIXL; M(AboveOrEqual) \ M(Add) \ M(And) \ + M(ArrayGet) \ M(ArrayLength) \ + M(ArraySet) \ M(Below) \ M(BelowOrEqual) \ + M(BooleanNot) \ + M(BoundsCheck) \ M(ClearException) \ M(ClinitCheck) \ M(Compare) \ M(CurrentMethod) \ + M(Deoptimize) \ M(Div) \ M(DivZeroCheck) \ M(DoubleConstant) \ @@ -154,6 +159,7 @@ class LoadClassSlowPathARMVIXL; M(Shl) \ M(Shr) \ M(StaticFieldGet) \ + M(StaticFieldSet) \ M(Sub) \ M(SuspendCheck) \ M(Throw) \ @@ -164,14 +170,9 @@ class LoadClassSlowPathARMVIXL; // TODO: Remove once the VIXL32 backend is implemented completely. #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ - M(ArrayGet) \ - M(ArraySet) \ - M(BooleanNot) \ - M(BoundsCheck) \ M(BoundType) \ M(CheckCast) \ M(ClassTableGet) \ - M(Deoptimize) \ M(InstanceOf) \ M(InvokeInterface) \ M(InvokeUnresolved) \ @@ -179,7 +180,6 @@ class LoadClassSlowPathARMVIXL; M(NativeDebugInfo) \ M(PackedSwitch) \ M(Rem) \ - M(StaticFieldSet) \ M(UnresolvedInstanceFieldGet) \ M(UnresolvedInstanceFieldSet) \ M(UnresolvedStaticFieldGet) \ @@ -438,6 +438,17 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // Helper method to move a 32-bit value between two locations. void Move32(Location destination, Location source); + void LoadFromShiftedRegOffset(Primitive::Type type, + Location out_loc, + vixl::aarch32::Register base, + vixl::aarch32::Register reg_index, + vixl::aarch32::Condition cond = vixl::aarch32::al); + void StoreToShiftedRegOffset(Primitive::Type type, + Location out_loc, + vixl::aarch32::Register base, + vixl::aarch32::Register reg_index, + vixl::aarch32::Condition cond = vixl::aarch32::al); + const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; } vixl::aarch32::Label* GetFrameEntryLabel() { return &frame_entry_label_; } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index f4a804f70c..12b1ab9abb 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5695,7 +5695,12 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { default: break; } - locations->SetOut(Location::RequiresRegister()); + if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetOut(calling_convention.GetReturnLocation(load->GetType())); + } else { + locations->SetOut(Location::RequiresRegister()); + } } void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) { diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h index 5d92bfd9cc..8c08a9c8b9 100644 --- a/compiler/optimizing/common_arm.h +++ b/compiler/optimizing/common_arm.h @@ -37,11 +37,6 @@ inline dwarf::Reg DWARFReg(vixl::aarch32::SRegister reg) { return dwarf::Reg::ArmFp(static_cast<int>(reg.GetCode())); } -inline vixl::aarch32::DRegister FromLowSToD(vixl::aarch32::SRegister reg) { - DCHECK_EQ(reg.GetCode() % 2, 0u) << reg; - return vixl::aarch32::DRegister(reg.GetCode() / 2); -} - inline vixl::aarch32::Register HighRegisterFrom(Location location) { DCHECK(location.IsRegisterPair()) << location; return vixl::aarch32::Register(location.AsRegisterPairHigh<vixl32::Register>()); @@ -135,6 +130,11 @@ inline vixl::aarch32::Register InputRegisterAt(HInstruction* instr, int input_in instr->InputAt(input_index)->GetType()); } +inline vixl::aarch32::Register InputRegister(HInstruction* instr) { + DCHECK_EQ(instr->InputCount(), 1u); + return InputRegisterAt(instr, 0); +} + inline int64_t Int64ConstantFrom(Location location) { HConstant* instr = location.GetConstant(); if (instr->IsIntConstant()) { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 8c769270b1..19fd6f95c3 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -170,6 +170,15 @@ class PassObserver : public ValueObject { if (visualizer_enabled_) { MutexLock mu(Thread::Current(), visualizer_dump_mutex_); *visualizer_output_ << visualizer_oss_.str(); + // The destructor of `visualizer_output_` is normally + // responsible for flushing (and closing) the stream, but it + // won't be invoked during fast exits in non-debug mode -- see + // art::Dex2Oat::~Dex2Oat, which explicitly abandons some + // objects (such as the compiler driver) in non-debug mode, to + // avoid the cost of destructing them. Therefore we explicitly + // flush the stream here to prevent truncated CFG visualizer + // files. + visualizer_output_->flush(); } } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index cd6ca4c6df..8d2a0e7860 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -399,6 +399,18 @@ class VerifierDepsTest : public CommonCompilerTest { has_unverified_classes; } + static std::set<VerifierDeps::MethodResolution>* GetMethods( + VerifierDeps::DexFileDeps* deps, MethodResolutionKind resolution_kind) { + if (resolution_kind == kDirectMethodResolution) { + return &deps->direct_methods_; + } else if (resolution_kind == kVirtualMethodResolution) { + return &deps->virtual_methods_; + } else { + DCHECK_EQ(resolution_kind, kInterfaceMethodResolution); + return &deps->interface_methods_; + } + } + std::unique_ptr<verifier::VerifierDeps> verifier_deps_; std::vector<const DexFile*> dex_files_; const DexFile* primary_dex_file_; @@ -1076,7 +1088,7 @@ TEST_F(VerifierDepsTest, EncodeDecode) { verifier_deps_->Encode(dex_files_, &buffer); ASSERT_FALSE(buffer.empty()); - VerifierDeps decoded_deps(dex_files_, ArrayRef<uint8_t>(buffer)); + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); ASSERT_TRUE(verifier_deps_->Equals(decoded_deps)); } @@ -1102,7 +1114,7 @@ TEST_F(VerifierDepsTest, EncodeDecodeMulti) { } // Dump the new verifier deps to ensure it can properly read the data. - VerifierDeps decoded_deps(dex_files, ArrayRef<uint8_t>(buffer)); + VerifierDeps decoded_deps(dex_files, ArrayRef<const uint8_t>(buffer)); std::ostringstream stream; VariableIndentationOutputStream os(&stream); decoded_deps.Dump(&os); @@ -1121,5 +1133,311 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) { ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;")); } +// Returns the next resolution kind in the enum. +static MethodResolutionKind GetNextResolutionKind(MethodResolutionKind resolution_kind) { + if (resolution_kind == kDirectMethodResolution) { + return kVirtualMethodResolution; + } else if (resolution_kind == kVirtualMethodResolution) { + return kInterfaceMethodResolution; + } else { + DCHECK_EQ(resolution_kind, kInterfaceMethodResolution); + return kDirectMethodResolution; + } +} + +TEST_F(VerifierDepsTest, VerifyDeps) { + VerifyDexFile(); + + ASSERT_EQ(1u, NumberOfCompiledDexFiles()); + ASSERT_TRUE(HasEachKindOfRecord()); + + // When validating, we create a new class loader, as + // the existing `class_loader_` may contain erroneous classes, + // that ClassLinker::FindClass won't return. + + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + MutableHandle<mirror::ClassLoader> new_class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr)); + { + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_TRUE(verifier_deps_->Verify(new_class_loader, soa.Self())); + } + + std::vector<uint8_t> buffer; + verifier_deps_->Encode(dex_files_, &buffer); + ASSERT_FALSE(buffer.empty()); + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_TRUE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + // Fiddle with the dependencies to make sure we catch any change and fail to verify. + + { + // Mess up with the assignable_types. + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + deps->assignable_types_.insert(*deps->unassignable_types_.begin()); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + // Mess up with the unassignable_types. + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + deps->unassignable_types_.insert(*deps->assignable_types_.begin()); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + // Mess up with classes. + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + for (const auto& entry : deps->classes_) { + if (entry.IsResolved()) { + deps->classes_.insert(VerifierDeps::ClassResolution( + entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker)); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + for (const auto& entry : deps->classes_) { + if (!entry.IsResolved()) { + deps->classes_.insert(VerifierDeps::ClassResolution( + entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1)); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + for (const auto& entry : deps->classes_) { + if (entry.IsResolved()) { + deps->classes_.insert(VerifierDeps::ClassResolution( + entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1)); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + // Mess up with fields. + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + for (const auto& entry : deps->fields_) { + if (entry.IsResolved()) { + deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(), + VerifierDeps::kUnresolvedMarker, + entry.GetDeclaringClassIndex())); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + for (const auto& entry : deps->fields_) { + if (!entry.IsResolved()) { + deps->fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */, + VerifierDeps::kUnresolvedMarker - 1, + 0 /* we know there is a class there */)); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + for (const auto& entry : deps->fields_) { + if (entry.IsResolved()) { + deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(), + entry.GetAccessFlags() - 1, + entry.GetDeclaringClassIndex())); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + for (const auto& entry : deps->fields_) { + static constexpr uint32_t kNewTypeIndex = 0; + if (entry.GetDeclaringClassIndex() != kNewTypeIndex) { + deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(), + entry.GetAccessFlags(), + kNewTypeIndex)); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + // Mess up with methods. + for (MethodResolutionKind resolution_kind : + { kDirectMethodResolution, kVirtualMethodResolution, kInterfaceMethodResolution }) { + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); + for (const auto& entry : *methods) { + if (entry.IsResolved()) { + methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + VerifierDeps::kUnresolvedMarker, + entry.GetDeclaringClassIndex())); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); + for (const auto& entry : *methods) { + if (!entry.IsResolved()) { + methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */, + VerifierDeps::kUnresolvedMarker - 1, + 0 /* we know there is a class there */)); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); + for (const auto& entry : *methods) { + if (entry.IsResolved()) { + methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + entry.GetAccessFlags() - 1, + entry.GetDeclaringClassIndex())); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); + for (const auto& entry : *methods) { + static constexpr uint32_t kNewTypeIndex = 0; + if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) { + methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + entry.GetAccessFlags(), + kNewTypeIndex)); + found = true; + break; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); + for (const auto& entry : *methods) { + if (entry.IsResolved()) { + GetMethods(deps, GetNextResolutionKind(resolution_kind))->insert( + VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + entry.GetAccessFlags(), + entry.GetDeclaringClassIndex())); + found = true; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); + for (const auto& entry : *methods) { + if (entry.IsResolved()) { + GetMethods(deps, GetNextResolutionKind(GetNextResolutionKind(resolution_kind)))->insert( + VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + entry.GetAccessFlags(), + entry.GetDeclaringClassIndex())); + found = true; + } + } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self())); + } + } +} + } // namespace verifier } // namespace art diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index da0db01386..3d208b5d28 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -66,6 +66,7 @@ #include "type_lookup_table.h" #include "vdex_file.h" #include "verifier/method_verifier.h" +#include "verifier/verifier_deps.h" #include "well_known_classes.h" #include <sys/stat.h> @@ -483,6 +484,28 @@ class OatDumper { os << "\n"; if (!options_.dump_header_only_) { + VariableIndentationOutputStream vios(&os); + VdexFile::Header vdex_header = oat_file_.GetVdexFile()->GetHeader(); + if (vdex_header.IsValid()) { + std::string error_msg; + std::vector<const DexFile*> dex_files; + for (size_t i = 0; i < oat_dex_files_.size(); i++) { + const DexFile* dex_file = OpenDexFile(oat_dex_files_[i], &error_msg); + if (dex_file == nullptr) { + os << "Error opening dex file: " << error_msg << std::endl; + return false; + } + dex_files.push_back(dex_file); + } + verifier::VerifierDeps deps(dex_files, oat_file_.GetVdexFile()->GetVerifierDepsData()); + deps.Dump(&vios); + } else { + os << "UNRECOGNIZED vdex file, magic " + << vdex_header.GetMagic() + << ", version " + << vdex_header.GetVersion() + << "\n"; + } for (size_t i = 0; i < oat_dex_files_.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 4638c3fc55..c151f00289 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -819,34 +819,60 @@ TEST_F(StubTest, CheckCast) { ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<4> hs(soa.Self()); Handle<mirror::Class> c( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"))); Handle<mirror::Class> c2( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"))); + Handle<mirror::Class> list( + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/List;"))); + Handle<mirror::Class> array_list( + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/ArrayList;"))); EXPECT_FALSE(self->IsExceptionPending()); - Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c.Get()), 0U, - art_quick_check_cast, self); - + Invoke3(reinterpret_cast<size_t>(c.Get()), + reinterpret_cast<size_t>(c.Get()), + 0U, + art_quick_check_cast, + self); EXPECT_FALSE(self->IsExceptionPending()); - Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c2.Get()), 0U, - art_quick_check_cast, self); - + Invoke3(reinterpret_cast<size_t>(c2.Get()), + reinterpret_cast<size_t>(c2.Get()), + 0U, + art_quick_check_cast, + self); EXPECT_FALSE(self->IsExceptionPending()); - Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c2.Get()), 0U, - art_quick_check_cast, self); - + Invoke3(reinterpret_cast<size_t>(c.Get()), + reinterpret_cast<size_t>(c2.Get()), + 0U, + art_quick_check_cast, + self); EXPECT_FALSE(self->IsExceptionPending()); - // TODO: Make the following work. But that would require correct managed frames. + Invoke3(reinterpret_cast<size_t>(list.Get()), + reinterpret_cast<size_t>(array_list.Get()), + 0U, + art_quick_check_cast, + self); + EXPECT_FALSE(self->IsExceptionPending()); - Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c.Get()), 0U, - art_quick_check_cast, self); + Invoke3(reinterpret_cast<size_t>(list.Get()), + reinterpret_cast<size_t>(c2.Get()), + 0U, + art_quick_check_cast, + self); + EXPECT_TRUE(self->IsExceptionPending()); + self->ClearException(); + // TODO: Make the following work. But that would require correct managed frames. + Invoke3(reinterpret_cast<size_t>(c2.Get()), + reinterpret_cast<size_t>(c.Get()), + 0U, + art_quick_check_cast, + self); EXPECT_TRUE(self->IsExceptionPending()); self->ClearException(); diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index afa1c0ff03..2856766fec 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1481,6 +1481,32 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline END_FUNCTION art_quick_unlock_object_no_inline DEFINE_FUNCTION art_quick_check_cast + testl LITERAL(ACCESS_FLAGS_CLASS_IS_INTERFACE), MIRROR_CLASS_ACCESS_FLAGS_OFFSET(%rdi) + jz .Lnot_interface + + // There are no read barriers since the iftable is immutable. There can be false negatives for + // the read barrier case if classes in the IfTable are in the from-space. In the case where + // we do not find a matching interface we call into artIsAssignableFromCode which will have + // read barriers. + movl MIRROR_CLASS_IF_TABLE_OFFSET(%rsi), %ecx + UNPOISON_HEAP_REF ecx + testl %ecx, %ecx + jz .Lnot_interface + movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), %r8d +.Lstart_loop: + // Re-poison before comparing to prevent rare possible false positives. This is done inside + // the loop since heap poisoning is only for testing builds. + POISON_HEAP_REF edi + cmpl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rcx), %edi + je .Lreturn // Return if same class. + UNPOISON_HEAP_REF edi + // Go to next interface. + add LITERAL(COMPRESSED_REFERENCE_SIZE * 2), %rcx + sub LITERAL(2), %r8 + jnz .Lstart_loop + +.Lnot_interface: + // We could check the super classes here but that is usually already checked in the caller. PUSH rdi // Save args for exc PUSH rsi subq LITERAL(8), %rsp // Alignment padding. @@ -1493,6 +1519,7 @@ DEFINE_FUNCTION art_quick_check_cast addq LITERAL(24), %rsp // pop arguments CFI_ADJUST_CFA_OFFSET(-24) +.Lreturn: ret CFI_ADJUST_CFA_OFFSET(24 + 4 * 8) // Reset unwind info so following code unwinds. diff --git a/runtime/asm_support.h b/runtime/asm_support.h index cd8815b25a..1e5e127ef9 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -172,6 +172,9 @@ ADD_TEST_EQ(size_t(MIRROR_OBJECT_HEADER_SIZE), sizeof(art::mirror::Object)) #define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET, art::mirror::Class::ComponentTypeOffset().Int32Value()) +#define MIRROR_CLASS_IF_TABLE_OFFSET (12 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET, + art::mirror::Class::IfTableOffset().Int32Value()) #define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET, art::mirror::Class::AccessFlagsOffset().Int32Value()) diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index 03f5bf6dd4..6c189b0a84 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -52,6 +52,8 @@ DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_LOCK_WORD_OFFSET), (static_ca DEFINE_CHECK_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), (static_cast<uint32_t>((art::mirror::Class::kStatusInitialized)))) #define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000 DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), (static_cast<uint32_t>((art::kAccClassIsFinalizable)))) +#define ACCESS_FLAGS_CLASS_IS_INTERFACE 0x200 +DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_INTERFACE), (static_cast<uint32_t>((art::kAccInterface)))) #define ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT 0x1f DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (static_cast<uint32_t>((art::MostSignificantBit(art::kAccClassIsFinalizable))))) #define ART_METHOD_DEX_CACHE_METHODS_OFFSET_32 20 diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 9992a9e29f..bbdb2af16a 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -526,8 +526,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline IfTable* Class::GetIfTable() { - return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>( - OFFSET_OF_OBJECT_MEMBER(Class, iftable_)); + return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(IfTableOffset()); } inline int32_t Class::GetIfTableCount() { @@ -539,7 +538,7 @@ inline int32_t Class::GetIfTableCount() { } inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) { - SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable); + SetFieldObject<false>(IfTableOffset(), new_iftable); } inline LengthPrefixedArray<ArtField>* Class::GetIFieldsPtr() { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 57937950a6..57bb2ed410 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -561,7 +561,7 @@ class MANAGED Class FINAL : public Object { // The size of java.lang.Class.class. static uint32_t ClassClassSize(PointerSize pointer_size) { // The number of vtable entries in java.lang.Class. - uint32_t vtable_entries = Object::kVTableLength + 72; + uint32_t vtable_entries = Object::kVTableLength + 73; return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size); } @@ -680,6 +680,10 @@ class MANAGED Class FINAL : public Object { return MemberOffset(OFFSETOF_MEMBER(Class, dex_cache_)); } + static MemberOffset IfTableOffset() { + return MemberOffset(OFFSETOF_MEMBER(Class, iftable_)); + } + enum { kDumpClassFullDetail = 1, kDumpClassClassLoader = (1 << 1), diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index df3865b1fe..c7a123b580 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -174,14 +174,9 @@ inline void VisitDexCachePairs(std::atomic<DexCachePair<T>>* pairs, // tell the compiler to treat "Read" as a template rather than a field or // function. Otherwise, on encountering the "<" token, the compiler would // treat "Read" as a field. - T* before = source.object.template Read<kReadBarrierOption>(); - // TODO(narayan): This additional GC root construction and assignment - // is unnecessary. We're already operating on a copy of the DexCachePair - // that's in the cache. - GcRoot<T> root(before); - visitor.VisitRootIfNonNull(root.AddressWithoutBarrier()); - if (root.Read() != before) { - source.object = GcRoot<T>(root.Read()); + T* const before = source.object.template Read<kReadBarrierOption>(); + visitor.VisitRootIfNonNull(source.object.AddressWithoutBarrier()); + if (source.object.template Read<kReadBarrierOption>() != before) { pairs[i].store(source, std::memory_order_relaxed); } } diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index 5095cfd790..b323aef0db 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -18,10 +18,10 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: ["events.cc", - "heap.cc", "object_tagging.cc", "OpenjdkJvmTi.cc", "ti_class.cc", + "ti_heap.cc", "ti_method.cc", "ti_stack.cc", "transform.cc"], diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 50b50d6661..d9031ea652 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -39,7 +39,6 @@ #include "art_jvmti.h" #include "base/mutex.h" #include "events-inl.h" -#include "heap.h" #include "jni_env_ext-inl.h" #include "object_tagging.h" #include "obj_ptr-inl.h" @@ -48,6 +47,7 @@ #include "thread_list.h" #include "thread-inl.h" #include "ti_class.h" +#include "ti_heap.h" #include "ti_method.h" #include "ti_stack.h" #include "transform.h" @@ -346,7 +346,7 @@ class JvmtiFunctions { } static jvmtiError ForceGarbageCollection(jvmtiEnv* env) { - return ERR(NOT_IMPLEMENTED); + return HeapUtil::ForceGarbageCollection(env); } static jvmtiError IterateOverObjectsReachableFromObject( diff --git a/runtime/openjdkjvmti/heap.cc b/runtime/openjdkjvmti/ti_heap.cc index 1799e1957d..6b207430ff 100644 --- a/runtime/openjdkjvmti/heap.cc +++ b/runtime/openjdkjvmti/ti_heap.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "heap.h" +#include "ti_heap.h" #include "art_jvmti.h" #include "base/macros.h" @@ -210,4 +210,10 @@ jvmtiError HeapUtil::GetLoadedClasses(jvmtiEnv* env, return ERR(NONE); } +jvmtiError HeapUtil::ForceGarbageCollection(jvmtiEnv* env ATTRIBUTE_UNUSED) { + art::Runtime::Current()->GetHeap()->CollectGarbage(false); + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/heap.h b/runtime/openjdkjvmti/ti_heap.h index b6becb97bb..570dd0c6ce 100644 --- a/runtime/openjdkjvmti/heap.h +++ b/runtime/openjdkjvmti/ti_heap.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_OPENJDKJVMTI_HEAP_H_ -#define ART_RUNTIME_OPENJDKJVMTI_HEAP_H_ +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_ #include "jvmti.h" @@ -36,6 +36,8 @@ class HeapUtil { const jvmtiHeapCallbacks* callbacks, const void* user_data); + static jvmtiError ForceGarbageCollection(jvmtiEnv* env); + ObjectTagTable* GetTags() { return tags_; } @@ -46,4 +48,4 @@ class HeapUtil { } // namespace openjdkjvmti -#endif // ART_RUNTIME_OPENJDKJVMTI_HEAP_H_ +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_ diff --git a/runtime/runtime.h b/runtime/runtime.h index 86464abe0d..77ec23812a 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -316,8 +316,7 @@ class Runtime { } bool IsMethodHandlesEnabled() const { - // return experimental_flags_ & ExperimentalFlags::kMethodHandles; - return true; + return experimental_flags_ & ExperimentalFlags::kMethodHandles; } void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 28f9bb3481..edd6ffe89d 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <string> +#include "base/array_ref.h" #include "base/macros.h" #include "mem_map.h" #include "os.h" @@ -44,8 +45,11 @@ class VdexFile { public: Header(uint32_t dex_size, uint32_t verifier_deps_size, uint32_t quickening_info_size); + const char* GetMagic() const { return reinterpret_cast<const char*>(magic_); } + const char* GetVersion() const { return reinterpret_cast<const char*>(version_); } bool IsMagicValid() const; bool IsVersionValid() const; + bool IsValid() const { return IsMagicValid() && IsVersionValid(); } uint32_t GetDexSize() const { return dex_size_; } uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; } @@ -71,6 +75,15 @@ class VdexFile { const uint8_t* End() const { return mmap_->End(); } size_t Size() const { return mmap_->Size(); } + const Header& GetHeader() const { + return *reinterpret_cast<const Header*>(Begin()); + } + + ArrayRef<const uint8_t> GetVerifierDepsData() const { + return ArrayRef<const uint8_t>( + Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize()); + } + private: explicit VdexFile(MemMap* mmap) : mmap_(mmap) {} diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 69708713d6..bdf63cb45b 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -453,8 +453,15 @@ void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files, } } -VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data) +VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, + ArrayRef<const uint8_t> data) : VerifierDeps(dex_files) { + if (data.empty()) { + // Return eagerly, as the first thing we expect from VerifierDeps data is + // the number of created strings, even if there is no dependency. + // Currently, only the boot image does not have any VerifierDeps data. + return; + } const uint8_t* data_start = data.data(); const uint8_t* data_end = data_start + data.size(); for (const DexFile* dex_file : dex_files) { @@ -600,5 +607,266 @@ void VerifierDeps::Dump(VariableIndentationOutputStream* vios) const { } } +bool VerifierDeps::Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const { + for (const auto& entry : dex_deps_) { + if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self)) { + return false; + } + } + return true; +} + +// TODO: share that helper with other parts of the compiler that have +// the same lookup pattern. +static mirror::Class* FindClassAndClearException(ClassLinker* class_linker, + Thread* self, + const char* name, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* result = class_linker->FindClass(self, name, class_loader); + if (result == nullptr) { + DCHECK(self->IsExceptionPending()); + self->ClearException(); + } + return result; +} + +bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<TypeAssignability>& assignables, + bool expected_assignability, + Thread* self) const { + StackHandleScope<2> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr)); + MutableHandle<mirror::Class> destination(hs.NewHandle<mirror::Class>(nullptr)); + + for (const auto& entry : assignables) { + const std::string& destination_desc = GetStringFromId(dex_file, entry.GetDestination()); + destination.Assign( + FindClassAndClearException(class_linker, self, destination_desc.c_str(), class_loader)); + const std::string& source_desc = GetStringFromId(dex_file, entry.GetSource()); + source.Assign( + FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader)); + + if (destination.Get() == nullptr) { + LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc; + return false; + } + + if (source.Get() == nullptr) { + LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc; + return false; + } + + DCHECK(destination->IsResolved() && source->IsResolved()); + if (destination->IsAssignableFrom(source.Get()) != expected_assignability) { + LOG(INFO) << "VerifierDeps: Class " + << destination_desc + << (expected_assignability ? " not " : " ") + << "assignable from " + << source_desc; + return false; + } + } + return true; +} + +bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<ClassResolution>& classes, + Thread* self) const { + StackHandleScope<1> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr)); + for (const auto& entry : classes) { + const char* descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex()); + cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader)); + + if (entry.IsResolved()) { + if (cls.Get() == nullptr) { + LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor; + return false; + } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) { + LOG(INFO) << "VerifierDeps: Unexpected access flags on class " + << descriptor + << std::hex + << " (expected=" + << entry.GetAccessFlags() + << ", actual=" + << GetAccessFlags(cls.Get()) << ")" + << std::dec; + return false; + } + } else if (cls.Get() != nullptr) { + LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor; + return false; + } + } + return true; +} + +static std::string GetFieldDescription(const DexFile& dex_file, uint32_t index) { + const DexFile::FieldId& field_id = dex_file.GetFieldId(index); + return std::string(dex_file.GetFieldDeclaringClassDescriptor(field_id)) + + "->" + + dex_file.GetFieldName(field_id) + + ":" + + dex_file.GetFieldTypeDescriptor(field_id); +} + +bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<FieldResolution>& fields, + Thread* self) const { + // Check recorded fields are resolved the same way, have the same recorded class, + // and have the same recorded flags. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + StackHandleScope<1> hs(self); + Handle<mirror::DexCache> dex_cache( + hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false))); + for (const auto& entry : fields) { + ArtField* field = class_linker->ResolveFieldJLS( + dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader); + + if (field == nullptr) { + DCHECK(self->IsExceptionPending()); + self->ClearException(); + } + + if (entry.IsResolved()) { + std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex()); + std::string temp; + if (field == nullptr) { + LOG(INFO) << "VerifierDeps: Could not resolve field " + << GetFieldDescription(dex_file, entry.GetDexFieldIndex()); + return false; + } else if (expected_decl_klass != field->GetDeclaringClass()->GetDescriptor(&temp)) { + LOG(INFO) << "VerifierDeps: Unexpected declaring class for field resolution " + << GetFieldDescription(dex_file, entry.GetDexFieldIndex()) + << " (expected=" << expected_decl_klass + << ", actual=" << field->GetDeclaringClass()->GetDescriptor(&temp) << ")"; + return false; + } else if (entry.GetAccessFlags() != GetAccessFlags(field)) { + LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved field " + << GetFieldDescription(dex_file, entry.GetDexFieldIndex()) + << std::hex << " (expected=" << entry.GetAccessFlags() + << ", actual=" << GetAccessFlags(field) << ")" << std::dec; + return false; + } + } else if (field != nullptr) { + LOG(INFO) << "VerifierDeps: Unexpected successful resolution of field " + << GetFieldDescription(dex_file, entry.GetDexFieldIndex()); + return false; + } + } + return true; +} + +static std::string GetMethodDescription(const DexFile& dex_file, uint32_t index) { + const DexFile::MethodId& method_id = dex_file.GetMethodId(index); + return std::string(dex_file.GetMethodDeclaringClassDescriptor(method_id)) + + "->" + + dex_file.GetMethodName(method_id) + + dex_file.GetMethodSignature(method_id).ToString(); +} + +bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<MethodResolution>& methods, + MethodResolutionKind kind, + Thread* self) const { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + PointerSize pointer_size = class_linker->GetImagePointerSize(); + + for (const auto& entry : methods) { + const DexFile::MethodId& method_id = dex_file.GetMethodId(entry.GetDexMethodIndex()); + + const char* name = dex_file.GetMethodName(method_id); + const Signature signature = dex_file.GetMethodSignature(method_id); + const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id); + + mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader); + if (cls == nullptr) { + LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor; + return false; + } + DCHECK(cls->IsResolved()); + ArtMethod* method = nullptr; + if (kind == kDirectMethodResolution) { + method = cls->FindDirectMethod(name, signature, pointer_size); + } else if (kind == kVirtualMethodResolution) { + method = cls->FindVirtualMethod(name, signature, pointer_size); + } else { + DCHECK_EQ(kind, kInterfaceMethodResolution); + method = cls->FindInterfaceMethod(name, signature, pointer_size); + } + + if (entry.IsResolved()) { + std::string temp; + std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex()); + if (method == nullptr) { + LOG(INFO) << "VerifierDeps: Could not resolve " + << kind + << " method " + << GetMethodDescription(dex_file, entry.GetDexMethodIndex()); + return false; + } else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) { + LOG(INFO) << "VerifierDeps: Unexpected declaring class for " + << kind + << " method resolution " + << GetMethodDescription(dex_file, entry.GetDexMethodIndex()) + << " (expected=" + << expected_decl_klass + << ", actual=" + << method->GetDeclaringClass()->GetDescriptor(&temp) + << ")"; + return false; + } else if (entry.GetAccessFlags() != GetAccessFlags(method)) { + LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved " + << kind + << " method resolution " + << GetMethodDescription(dex_file, entry.GetDexMethodIndex()) + << std::hex + << " (expected=" + << entry.GetAccessFlags() + << ", actual=" + << GetAccessFlags(method) << ")" + << std::dec; + return false; + } + } else if (method != nullptr) { + LOG(INFO) << "VerifierDeps: Unexpected successful resolution of " + << kind + << " method " + << GetMethodDescription(dex_file, entry.GetDexMethodIndex()); + return false; + } + } + return true; +} + +bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const DexFileDeps& deps, + Thread* self) const { + bool result = VerifyAssignability( + class_loader, dex_file, deps.assignable_types_, /* expected_assignability */ true, self); + result = result && VerifyAssignability( + class_loader, dex_file, deps.unassignable_types_, /* expected_assignability */ false, self); + + result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self); + result = result && VerifyFields(class_loader, dex_file, deps.fields_, self); + + result = result && VerifyMethods( + class_loader, dex_file, deps.direct_methods_, kDirectMethodResolution, self); + result = result && VerifyMethods( + class_loader, dex_file, deps.virtual_methods_, kVirtualMethodResolution, self); + result = result && VerifyMethods( + class_loader, dex_file, deps.interface_methods_, kInterfaceMethodResolution, self); + + return result; +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index 6b0c959c08..eea0299f6b 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -51,6 +51,10 @@ class VerifierDeps { explicit VerifierDeps(const std::vector<const DexFile*>& dex_files) REQUIRES(!Locks::verifier_deps_lock_); + VerifierDeps(const std::vector<const DexFile*>& dex_files, + ArrayRef<const uint8_t> data) + REQUIRES(!Locks::verifier_deps_lock_); + // Record the verification status of the class at `type_idx`. static void MaybeRecordVerificationStatus(const DexFile& dex_file, uint16_t type_idx, @@ -105,13 +109,14 @@ class VerifierDeps { void Dump(VariableIndentationOutputStream* vios) const NO_THREAD_SAFETY_ANALYSIS; + // Verify the encoded dependencies of this `VerifierDeps`. + // NO_THREAD_SAFETY_ANALYSIS, as this must be called on a read-only `VerifierDeps`. + bool Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const + NO_THREAD_SAFETY_ANALYSIS; + private: static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1); - // Only used in tests to reconstruct the data structure from serialized data. - VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data) - REQUIRES(!Locks::verifier_deps_lock_); - using ClassResolutionBase = std::tuple<uint32_t, uint16_t>; struct ClassResolution : public ClassResolutionBase { ClassResolution() = default; @@ -255,6 +260,54 @@ class VerifierDeps { bool Equals(const VerifierDeps& rhs) const REQUIRES(!Locks::verifier_deps_lock_); + // Verify `dex_file` according to the `deps`, that is going over each + // `DexFileDeps` field, and checking that the recorded information still + // holds. + bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const DexFileDeps& deps, + Thread* self) const + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::verifier_deps_lock_); + + bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<TypeAssignability>& assignables, + bool expected_assignability, + Thread* self) const + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::verifier_deps_lock_); + + // Verify that the set of resolved classes at the point of creation + // of this `VerifierDeps` is still the same. + bool VerifyClasses(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<ClassResolution>& classes, + Thread* self) const + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::verifier_deps_lock_); + + // Verify that the set of resolved fields at the point of creation + // of this `VerifierDeps` is still the same, and each field resolves to the + // same field holder and access flags. + bool VerifyFields(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<FieldResolution>& classes, + Thread* self) const + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::verifier_deps_lock_); + + // Verify that the set of resolved methods at the point of creation + // of this `VerifierDeps` is still the same, and each method resolves to the + // same method holder, access flags, and invocation kind. + bool VerifyMethods(Handle<mirror::ClassLoader> class_loader, + const DexFile& dex_file, + const std::set<MethodResolution>& methods, + MethodResolutionKind kind, + Thread* self) const + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::verifier_deps_lock_); + // Map from DexFiles into dependencies collected from verification of their methods. std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_ GUARDED_BY(Locks::verifier_deps_lock_); @@ -263,6 +316,7 @@ class VerifierDeps { ART_FRIEND_TEST(VerifierDepsTest, StringToId); ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode); ART_FRIEND_TEST(VerifierDepsTest, EncodeDecodeMulti); + ART_FRIEND_TEST(VerifierDepsTest, VerifyDeps); }; } // namespace verifier diff --git a/test/913-heaps/build b/test/913-heaps/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/913-heaps/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 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. + +./default-build "$@" --experimental agents diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt new file mode 100644 index 0000000000..77791a4efe --- /dev/null +++ b/test/913-heaps/expected.txt @@ -0,0 +1,2 @@ +--- +true true diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc new file mode 100644 index 0000000000..437779a5f4 --- /dev/null +++ b/test/913-heaps/heaps.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 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 "heaps.h" + +#include <stdio.h> +#include <string.h> + +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" + +#include "ti-agent/common_load.h" + +namespace art { +namespace Test913Heaps { + +extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env ATTRIBUTE_UNUSED, + jclass klass ATTRIBUTE_UNUSED) { + jvmtiError ret = jvmti_env->ForceGarbageCollection(); + if (ret != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(ret, &err); + printf("Error forcing a garbage collection: %s\n", err); + } +} + +// Don't do anything +jint OnLoad(JavaVM* vm, + char* options ATTRIBUTE_UNUSED, + void* reserved ATTRIBUTE_UNUSED) { + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + printf("Unable to get jvmti env!\n"); + return 1; + } + return 0; +} + +} // namespace Test913Heaps +} // namespace art diff --git a/test/913-heaps/heaps.h b/test/913-heaps/heaps.h new file mode 100644 index 0000000000..bd828aca33 --- /dev/null +++ b/test/913-heaps/heaps.h @@ -0,0 +1,30 @@ +/* + * 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_TEST_913_HEAPS_HEAPS_H_ +#define ART_TEST_913_HEAPS_HEAPS_H_ + +#include <jni.h> + +namespace art { +namespace Test913Heaps { + +jint OnLoad(JavaVM* vm, char* options, void* reserved); + +} // namespace Test913Heaps +} // namespace art + +#endif // ART_TEST_913_HEAPS_HEAPS_H_ diff --git a/test/913-heaps/info.txt b/test/913-heaps/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/913-heaps/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/913-heaps/run b/test/913-heaps/run new file mode 100755 index 0000000000..7bd8cbd1dd --- /dev/null +++ b/test/913-heaps/run @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Copyright 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. + +plugin=libopenjdkjvmtid.so +agent=libtiagentd.so +lib=tiagentd +if [[ "$@" == *"-O"* ]]; then + agent=libtiagent.so + plugin=libopenjdkjvmti.so + lib=tiagent +fi + +if [[ "$@" == *"--jvm"* ]]; then + arg="jvm" +else + arg="art" +fi + +if [[ "$@" != *"--debuggable"* ]]; then + other_args=" -Xcompiler-option --debuggable " +else + other_args="" +fi + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --runtime-option -agentpath:${agent}=913-heaps,${arg} \ + --android-runtime-option -Xplugin:${plugin} \ + ${other_args} \ + --args ${lib} diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java new file mode 100644 index 0000000000..4d77a482b7 --- /dev/null +++ b/test/913-heaps/src/Main.java @@ -0,0 +1,57 @@ +/* + * 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. + */ + +import java.util.ArrayList; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + public static void doTest() throws Exception { + setupGcCallback(); + + enableGcTracking(true); + run(); + enableGcTracking(false); + } + + private static void run() { + clearStats(); + forceGarbageCollection(); + printStats(); + } + + private static void clearStats() { + getGcStarts(); + getGcFinishes(); + } + + private static void printStats() { + System.out.println("---"); + int s = getGcStarts(); + int f = getGcFinishes(); + System.out.println((s > 0) + " " + (f > 0)); + } + + private static native void setupGcCallback(); + private static native void enableGcTracking(boolean enable); + private static native int getGcStarts(); + private static native int getGcFinishes(); + private static native void forceGarbageCollection(); +} diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index 58ba55d6b3..44c0447771 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -58,6 +58,7 @@ public class Main { testfindSpecial_invokeSuperBehaviour(); testfindSpecial_invokeDirectBehaviour(); testExceptionDetailMessages(); + testfindVirtual(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -140,6 +141,127 @@ public class Main { System.out.println("Received exception: " + ex.getMessage()); } } + + public interface Foo { + public String foo(); + } + + public interface Bar extends Foo { + public String bar(); + } + + public static class BarSuper { + public String superPublicMethod() { + return "superPublicMethod"; + } + + public String superProtectedMethod() { + return "superProtectedMethod"; + } + + String superPackageMethod() { + return "superPackageMethod"; + } + } + + public static class BarImpl extends BarSuper implements Bar { + public BarImpl() { + } + + @Override + public String foo() { + return "foo"; + } + + @Override + public String bar() { + return "bar"; + } + + private String privateMethod() { return "privateMethod"; } + + public static String staticMethod() { return null; } + + static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + } + + public static void testfindVirtual() throws Throwable { + // Virtual lookups on static methods should not succeed. + try { + MethodHandles.lookup().findVirtual( + BarImpl.class, "staticMethod", MethodType.methodType(String.class)); + System.out.println("findVirtual(staticMethod) unexpectedly succeeded"); + } catch (IllegalAccessException expected) { + } + + // Virtual lookups on private methods should not succeed, unless the Lookup + // context had sufficient privileges. + try { + MethodHandles.lookup().findVirtual( + BarImpl.class, "privateMethod", MethodType.methodType(String.class)); + System.out.println("findVirtual(privateMethod) unexpectedly succeeded"); + } catch (IllegalAccessException expected) { + } + + // Virtual lookup on a private method with a context that *does* have sufficient + // privileges. + MethodHandle mh = BarImpl.lookup.findVirtual( + BarImpl.class, "privateMethod", MethodType.methodType(String.class)); + String str = (String) mh.invoke(new BarImpl()); + if (!"privateMethod".equals(str)) { + System.out.println("Unexpected return value for BarImpl#privateMethod: " + str); + } + + // Find virtual must find interface methods defined by interfaces implemented + // by the class. + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + if (!"foo".equals(str)) { + System.out.println("Unexpected return value for BarImpl#foo: " + str); + } + + // .. and their super-interfaces. + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + if (!"bar".equals(str)) { + System.out.println("Unexpected return value for BarImpl#bar: " + str); + } + + // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the + // invoke resulting in a failing check in the interpreter. + // + // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar", + // MethodType.methodType(String.class)); + // str = (String) mh.invoke(new BarImpl()); + // if (!"bar".equals(str)) { + // System.out.println("Unexpected return value for BarImpl#bar: " + str); + // } + + // We should also be able to lookup public / protected / package methods in + // the super class, given sufficient access privileges. + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPublicMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + if (!"superPublicMethod".equals(str)) { + System.out.println("Unexpected return value for BarImpl#superPublicMethod: " + str); + } + + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superProtectedMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + if (!"superProtectedMethod".equals(str)) { + System.out.println("Unexpected return value for BarImpl#superProtectedMethod: " + str); + } + + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPackageMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + if (!"superPackageMethod".equals(str)) { + System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str); + } + } } diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt index 73a34bc41b..fc4fdd6070 100644 --- a/test/957-methodhandle-transforms/expected.txt +++ b/test/957-methodhandle-transforms/expected.txt @@ -1,35 +1,16 @@ ---- --- testDelegation ---- -boolean: false -char: h -short: 56 -int: 72 -long: 2147483689 -float: 0.56 -double: 100.0 -String: hello -Object: goodbye -boolean: false -char: h -short: 56 -int: 72 -long: 73 -float: 0.56 -double: 100.0 -String: hello -Object: goodbye -true -true -a -a -42 -42 -43 -43 -43.0 -43.0 -43.0 -43.0 -plank -plank +Message: foo, Message2: 42 +Message: foo, Message2: 42 +Message: foo, Message2: 42 +Message: foo, Message2: 42 +Message: foo, Message2: 42 +Message: foo, Message2: 42 +Target: Arg1: foo, Arg2: 42 +Target: Arg1: foo, Arg2: 42 +Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo, Arg2: 42, ExMsg: exceptionMessage +Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo, Arg2: 42, ExMsg: exceptionMessage +Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo +Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo +target: target, 42, 56 +target: target, 42, 56 +fallback: fallback, 42, 56 +target: target, 42, 56 diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java index e9d313b874..4a27086924 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -19,182 +19,229 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.invoke.WrongMethodTypeException; -import java.lang.invoke.Transformers.Transformer; - -import dalvik.system.EmulatedStackFrame; public class Main { - - public static void testDelegate_allTypes(boolean z, char a, short b, int c, long d, - float e, double f, String g, Object h) { - System.out.println("boolean: " + z); - System.out.println("char: " + a); - System.out.println("short: " + b); - System.out.println("int: " + c); - System.out.println("long: " + d); - System.out.println("float: " + e); - System.out.println("double: " + f); - System.out.println("String: " + g); - System.out.println("Object: " + h); - } - - public static boolean testDelegate_returnBoolean() { - return true; - } - - public static char testDelegate_returnChar() { - return 'a'; - } - - public static int testDelegate_returnInt() { - return 42; + public static void main(String[] args) throws Throwable { + testThrowException(); + testDropArguments(); + testCatchException(); + testGuardWithTest(); } - public static long testDelegate_returnLong() { - return 43; - } + public static void testThrowException() throws Throwable { + MethodHandle handle = MethodHandles.throwException(String.class, + IllegalArgumentException.class); - public static float testDelegate_returnFloat() { - return 43.0f; - } + if (handle.type().returnType() != String.class) { + System.out.println("Unexpected return type for handle: " + handle + + " [ " + handle.type() + "]"); + } - public static double testDelegate_returnDouble() { - return 43.0; + final IllegalArgumentException iae = new IllegalArgumentException("boo!"); + try { + handle.invoke(iae); + System.out.println("Expected an exception of type: java.lang.IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + if (expected != iae) { + System.out.println("Wrong exception: expected " + iae + " but was " + expected); + } + } } - public static String testDelegate_returnString() { - return "plank"; + public static void dropArguments_delegate(String message, long message2) { + System.out.println("Message: " + message + ", Message2: " + message2); } - public static class DelegatingTransformer extends Transformer { - private final MethodHandle delegate; + public static void testDropArguments() throws Throwable { + MethodHandle delegate = MethodHandles.lookup().findStatic(Main.class, + "dropArguments_delegate", + MethodType.methodType(void.class, new Class<?>[] { String.class, long.class })); - public DelegatingTransformer(MethodHandle delegate) { - super(delegate.type()); - this.delegate = delegate; - } + MethodHandle transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class); - @Override - public void transform(EmulatedStackFrame stackFrame) throws Throwable { - delegate.invoke(stackFrame); + // The transformer will accept two additional arguments at position zero. + try { + transform.invokeExact("foo", 42l); + fail(); + } catch (WrongMethodTypeException expected) { } - } - - public static void main(String[] args) throws Throwable { - testThrowException(); - - testDelegation(); - } - public static void testDelegation() throws Throwable { - System.out.println("---"); - System.out.println("-- testDelegation"); - System.out.println("---"); + transform.invokeExact(45, new Object(), "foo", 42l); + transform.invoke(45, new Object(), "foo", 42l); - MethodHandle specialFunctionHandle = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_allTypes", MethodType.methodType(void.class, - new Class<?>[] { boolean.class, char.class, short.class, int.class, long.class, - float.class, double.class, String.class, Object.class })); + // Additional arguments at position 1. + transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class); + transform.invokeExact("foo", 45, new Object(), 42l); + transform.invoke("foo", 45, new Object(), 42l); - DelegatingTransformer delegate = new DelegatingTransformer(specialFunctionHandle); + // Additional arguments at position 2. + transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class); + transform.invokeExact("foo", 42l, 45, new Object()); + transform.invoke("foo", 42l, 45, new Object()); - // Test an exact invoke. - // - // Note that the shorter form below doesn't work and must be - // investigated on the jack side : b/32536744 - // - // delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l, - // 0.56f, 100.0d, "hello", (Object) "goodbye"); + // Note that we still perform argument conversions even for the arguments that + // are subsequently dropped. + try { + transform.invoke("foo", 42l, 45l, new Object()); + fail(); + } catch (WrongMethodTypeException expected) { + } catch (IllegalArgumentException expected) { + // TODO(narayan): We currently throw the wrong type of exception here, + // it's IAE and should be WMTE instead. + } - Object obj = "goodbye"; - delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l, - 0.56f, 100.0d, "hello", obj); + // Invalid argument location, should not be allowed. + try { + MethodHandles.dropArguments(delegate, -1, int.class, Object.class); + fail(); + } catch (IllegalArgumentException expected) { + } - // Test a non exact invoke with one int -> long conversion and a float -> double - // conversion. - delegate.invoke(false, 'h', (short) 56, 72, 73, - 0.56f, 100.0f, "hello", "goodbye"); + // Invalid argument location, should not be allowed. + try { + MethodHandles.dropArguments(delegate, 3, int.class, Object.class); + fail(); + } catch (IllegalArgumentException expected) { + } - // Should throw a WrongMethodTypeException if the types don't align. try { - delegate.invoke(false); - throw new AssertionError("Call to invoke unexpectedly succeeded"); - } catch (WrongMethodTypeException expected) { + MethodHandles.dropArguments(delegate, 1, void.class); + fail(); + } catch (IllegalArgumentException expected) { } + } - // Test return values. + public static String testCatchException_target(String arg1, long arg2, String exceptionMessage) + throws Throwable { + if (exceptionMessage != null) { + throw new IllegalArgumentException(exceptionMessage); + } - // boolean. - MethodHandle returner = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_returnBoolean", MethodType.methodType(boolean.class)); - delegate = new DelegatingTransformer(returner); + System.out.println("Target: Arg1: " + arg1 + ", Arg2: " + arg2); + return "target"; + } - System.out.println((boolean) delegate.invoke()); - System.out.println((boolean) delegate.invokeExact()); + public static String testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2, + String exMsg) { + System.out.println("Handler: " + iae + ", Arg1: " + arg1 + ", Arg2: " + arg2 + ", ExMsg: " + exMsg); + return "handler1"; + } - // char. - returner = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_returnChar", MethodType.methodType(char.class)); - delegate = new DelegatingTransformer(returner); + public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) { + System.out.println("Handler: " + iae + ", Arg1: " + arg1); + return "handler2"; + } - System.out.println((char) delegate.invoke()); - System.out.println((char) delegate.invokeExact()); + public static void testCatchException() throws Throwable { + MethodHandle target = MethodHandles.lookup().findStatic(Main.class, + "testCatchException_target", + MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class })); + + MethodHandle handler = MethodHandles.lookup().findStatic(Main.class, + "testCatchException_handler", + MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class, + String.class, long.class, String.class })); + + MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class, + handler); + + String returnVal = null; + + // These two should end up calling the target always. We're passing a null exception + // message here, which means the target will not throw. + returnVal = (String) adapter.invoke("foo", 42, null); + assertEquals("target", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, (String) null); + assertEquals("target", returnVal); + + // We're passing a non-null exception message here, which means the target will throw, + // which in turn means that the handler must be called for the next two invokes. + returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); + assertEquals("handler1", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); + assertEquals("handler1", returnVal); + + handler = MethodHandles.lookup().findStatic(Main.class, + "testCatchException_handler2", + MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class, + String.class })); + adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); + + returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); + assertEquals("handler2", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); + assertEquals("handler2", returnVal); + + // Test that the type of the invoke doesn't matter. Here we call + // IllegalArgumentException.toString() on the exception that was thrown by + // the target. + handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class, + "toString", MethodType.methodType(String.class)); + adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); + + returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); + assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); + returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2"); + assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal); + } - // int. - returner = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_returnInt", MethodType.methodType(int.class)); - delegate = new DelegatingTransformer(returner); + public static boolean testGuardWithTest_test(String arg1, long arg2) { + return "target".equals(arg1) && 42 == arg2; + } - System.out.println((int) delegate.invoke()); - System.out.println((int) delegate.invokeExact()); + public static String testGuardWithTest_target(String arg1, long arg2, int arg3) { + System.out.println("target: " + arg1 + ", " + arg2 + ", " + arg3); + return "target"; + } - // long. - returner = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_returnLong", MethodType.methodType(long.class)); - delegate = new DelegatingTransformer(returner); + public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) { + System.out.println("fallback: " + arg1 + ", " + arg2 + ", " + arg3); + return "fallback"; + } - System.out.println((long) delegate.invoke()); - System.out.println((long) delegate.invokeExact()); + public static void testGuardWithTest() throws Throwable { + MethodHandle test = MethodHandles.lookup().findStatic(Main.class, + "testGuardWithTest_test", + MethodType.methodType(boolean.class, new Class<?>[] { String.class, long.class })); - // float. - returner = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_returnFloat", MethodType.methodType(float.class)); - delegate = new DelegatingTransformer(returner); + final MethodType type = MethodType.methodType(String.class, + new Class<?>[] { String.class, long.class, int.class }); - System.out.println((float) delegate.invoke()); - System.out.println((float) delegate.invokeExact()); + final MethodHandle target = MethodHandles.lookup().findStatic(Main.class, + "testGuardWithTest_target", type); + final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.class, + "testGuardWithTest_fallback", type); - // double. - returner = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_returnDouble", MethodType.methodType(double.class)); - delegate = new DelegatingTransformer(returner); + MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback); - System.out.println((double) delegate.invoke()); - System.out.println((double) delegate.invokeExact()); + String returnVal = null; - // references. - returner = MethodHandles.lookup().findStatic( - Main.class, "testDelegate_returnString", MethodType.methodType(String.class)); - delegate = new DelegatingTransformer(returner); + returnVal = (String) adapter.invoke("target", 42, 56); + assertEquals("target", returnVal); + returnVal = (String) adapter.invokeExact("target", 42l, 56); + assertEquals("target", returnVal); - System.out.println((String) delegate.invoke()); - System.out.println((String) delegate.invokeExact()); + returnVal = (String) adapter.invoke("fallback", 42l, 56); + assertEquals("fallback", returnVal); + returnVal = (String) adapter.invokeExact("target", 42l, 56); + assertEquals("target", returnVal); } - public static void testThrowException() throws Throwable { - MethodHandle handle = MethodHandles.throwException(String.class, - IllegalArgumentException.class); + public static void fail() { + System.out.println("FAIL"); + Thread.dumpStack(); + } - if (handle.type().returnType() != String.class) { - System.out.println("Unexpected return type for handle: " + handle + - " [ " + handle.type() + "]"); + public static void assertEquals(String s1, String s2) { + if (s1 == s2) { + return; } - try { - handle.invoke(); - System.out.println("Expected an exception of type: java.lang.IllegalArgumentException"); - } catch (IllegalArgumentException expected) { + if (s1 != null && s2 != null && s1.equals(s2)) { + return; } + + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); } } diff --git a/test/958-methodhandle-emulated-stackframe/build b/test/958-methodhandle-emulated-stackframe/build new file mode 100755 index 0000000000..a423ca6b4e --- /dev/null +++ b/test/958-methodhandle-emulated-stackframe/build @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright 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. + +# make us exit on a failure +set -e + +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm. + export USE_JACK=true +fi + +./default-build "$@" --experimental method-handles diff --git a/test/958-methodhandle-emulated-stackframe/expected.txt b/test/958-methodhandle-emulated-stackframe/expected.txt new file mode 100644 index 0000000000..5f3825962d --- /dev/null +++ b/test/958-methodhandle-emulated-stackframe/expected.txt @@ -0,0 +1,32 @@ +boolean: false +char: h +short: 56 +int: 72 +long: 2147483689 +float: 0.56 +double: 100.0 +String: hello +Object: goodbye +boolean: false +char: h +short: 56 +int: 72 +long: 73 +float: 0.56 +double: 100.0 +String: hello +Object: goodbye +true +true +a +a +42 +42 +43 +43 +43.0 +43.0 +43.0 +43.0 +plank +plank diff --git a/test/958-methodhandle-emulated-stackframe/info.txt b/test/958-methodhandle-emulated-stackframe/info.txt new file mode 100644 index 0000000000..bec2324e76 --- /dev/null +++ b/test/958-methodhandle-emulated-stackframe/info.txt @@ -0,0 +1,5 @@ +Tests for dalvik.system.EmulatedStackFrame, which is used to implement +MethodHandle transformations. This is a separate test because it tests +an implementation detail and hence cannot be used with --mode=jvm. + +NOTE: needs to run under ART or a Java 8 Language runtime and compiler. diff --git a/test/958-methodhandle-emulated-stackframe/run b/test/958-methodhandle-emulated-stackframe/run new file mode 100755 index 0000000000..a9f182288c --- /dev/null +++ b/test/958-methodhandle-emulated-stackframe/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 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. + +# make us exit on a failure +set -e + +./default-run "$@" --experimental method-handles diff --git a/test/958-methodhandle-emulated-stackframe/src/Main.java b/test/958-methodhandle-emulated-stackframe/src/Main.java new file mode 100644 index 0000000000..f739d47d08 --- /dev/null +++ b/test/958-methodhandle-emulated-stackframe/src/Main.java @@ -0,0 +1,175 @@ +/* + * 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. + */ + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; +import java.lang.invoke.Transformers.Transformer; + +import dalvik.system.EmulatedStackFrame; + +public class Main { + + public static void testDelegate_allTypes(boolean z, char a, short b, int c, long d, + float e, double f, String g, Object h) { + System.out.println("boolean: " + z); + System.out.println("char: " + a); + System.out.println("short: " + b); + System.out.println("int: " + c); + System.out.println("long: " + d); + System.out.println("float: " + e); + System.out.println("double: " + f); + System.out.println("String: " + g); + System.out.println("Object: " + h); + } + + public static boolean testDelegate_returnBoolean() { + return true; + } + + public static char testDelegate_returnChar() { + return 'a'; + } + + public static int testDelegate_returnInt() { + return 42; + } + + public static long testDelegate_returnLong() { + return 43; + } + + public static float testDelegate_returnFloat() { + return 43.0f; + } + + public static double testDelegate_returnDouble() { + return 43.0; + } + + public static String testDelegate_returnString() { + return "plank"; + } + + public static class DelegatingTransformer extends Transformer { + private final MethodHandle delegate; + + public DelegatingTransformer(MethodHandle delegate) { + super(delegate.type()); + this.delegate = delegate; + } + + @Override + public void transform(EmulatedStackFrame stackFrame) throws Throwable { + delegate.invoke(stackFrame); + } + } + + public static void main(String[] args) throws Throwable { + MethodHandle specialFunctionHandle = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_allTypes", MethodType.methodType(void.class, + new Class<?>[] { boolean.class, char.class, short.class, int.class, long.class, + float.class, double.class, String.class, Object.class })); + + DelegatingTransformer delegate = new DelegatingTransformer(specialFunctionHandle); + + // Test an exact invoke. + // + // Note that the shorter form below doesn't work and must be + // investigated on the jack side : b/32536744 + // + // delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l, + // 0.56f, 100.0d, "hello", (Object) "goodbye"); + + Object obj = "goodbye"; + delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l, + 0.56f, 100.0d, "hello", obj); + + // Test a non exact invoke with one int -> long conversion and a float -> double + // conversion. + delegate.invoke(false, 'h', (short) 56, 72, 73, + 0.56f, 100.0f, "hello", "goodbye"); + + // Should throw a WrongMethodTypeException if the types don't align. + try { + delegate.invoke(false); + throw new AssertionError("Call to invoke unexpectedly succeeded"); + } catch (WrongMethodTypeException expected) { + } + + // Test return values. + + // boolean. + MethodHandle returner = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_returnBoolean", MethodType.methodType(boolean.class)); + delegate = new DelegatingTransformer(returner); + + System.out.println((boolean) delegate.invoke()); + System.out.println((boolean) delegate.invokeExact()); + + // char. + returner = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_returnChar", MethodType.methodType(char.class)); + delegate = new DelegatingTransformer(returner); + + System.out.println((char) delegate.invoke()); + System.out.println((char) delegate.invokeExact()); + + // int. + returner = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_returnInt", MethodType.methodType(int.class)); + delegate = new DelegatingTransformer(returner); + + System.out.println((int) delegate.invoke()); + System.out.println((int) delegate.invokeExact()); + + // long. + returner = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_returnLong", MethodType.methodType(long.class)); + delegate = new DelegatingTransformer(returner); + + System.out.println((long) delegate.invoke()); + System.out.println((long) delegate.invokeExact()); + + // float. + returner = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_returnFloat", MethodType.methodType(float.class)); + delegate = new DelegatingTransformer(returner); + + System.out.println((float) delegate.invoke()); + System.out.println((float) delegate.invokeExact()); + + // double. + returner = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_returnDouble", MethodType.methodType(double.class)); + delegate = new DelegatingTransformer(returner); + + System.out.println((double) delegate.invoke()); + System.out.println((double) delegate.invokeExact()); + + // references. + returner = MethodHandles.lookup().findStatic( + Main.class, "testDelegate_returnString", MethodType.methodType(String.class)); + delegate = new DelegatingTransformer(returner); + + System.out.println((String) delegate.invoke()); + System.out.println((String) delegate.invokeExact()); + } +} + + diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk index 5f969e3d5b..2d9708de93 100644 --- a/test/Android.arm_vixl.mk +++ b/test/Android.arm_vixl.mk @@ -18,33 +18,23 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 002-sleep \ 003-omnibus-opcodes \ - 004-checker-UnsafeTest18 \ 004-InterfaceTest \ 004-JniTest \ 004-NativeAllocations \ - 004-ReferenceMap \ - 004-SignalTest \ - 004-StackWalk \ 004-ThreadStress \ 004-UnsafeTest \ + 004-checker-UnsafeTest18 \ 005-annotations \ - 006-args \ - 008-exceptions \ 009-instanceof \ - 011-array-copy \ 012-math \ 015-switch \ - 019-wrong-array-type \ - 020-string \ 021-string2 \ 022-interface \ 023-many-interfaces \ 024-illegal-access \ 025-access-controller \ - 027-arithmetic \ 028-array-write \ 031-class-attributes \ - 032-concrete-sub \ 035-enum \ 036-finalizer \ 037-inherit \ @@ -54,7 +44,6 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 046-reflect \ 047-returns \ 048-reflect-v8 \ - 049-show-object \ 050-sync-test \ 051-thread \ 052-verifier-fun \ @@ -62,7 +51,6 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 054-uncaught \ 055-enum-performance \ 058-enum-order \ - 059-finalizer-throw \ 061-out-of-memory \ 062-character-encodings \ 063-process-manager \ @@ -72,31 +60,21 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 067-preemptive-unpark \ 068-classloader \ 069-field-type \ - 070-nio-buffer \ 071-dexfile \ - 072-precise-gc \ 074-gc-thrash \ 075-verification-error \ - 076-boolean-put \ 079-phantom \ 080-oom-throw \ - 080-oom-throw-with-finalizer \ - 081-hot-exceptions \ 082-inline-execute \ 083-compiler-regressions \ 086-null-super \ 087-gc-after-link \ 088-monitor-verification \ - 090-loop-formation \ 091-override-package-private-method \ 093-serialization \ - 094-pattern \ 096-array-copy-concurrent-gc \ 098-ddmc \ 099-vmdebug \ - 100-reflect2 \ - 101-fibonacci \ - 102-concurrent-gc \ 103-string-append \ 104-growth-limit \ 106-exceptions2 \ @@ -105,8 +83,6 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 109-suspend-check \ 113-multidex \ 114-ParallelGC \ - 117-nopatchoat \ - 119-noimage-patchoat \ 120-hashcode \ 121-modifiers \ 122-npe \ @@ -114,7 +90,6 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 123-inline-execute2 \ 127-checker-secondarydex \ 129-ThreadGetId \ - 131-structural-change \ 132-daemon-locks-shutdown \ 133-static-invoke-super \ 134-reg-promotion \ @@ -122,33 +97,24 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 136-daemon-jni-shutdown \ 137-cfi \ 138-duplicate-classes-check2 \ - 139-register-natives \ 140-field-packing \ 141-class-unload \ 142-classloader2 \ 144-static-field-sigquit \ - 145-alloc-tracking-stress \ 146-bad-interface \ 150-loadlibrary \ 151-OpenFileLimit \ 201-built-in-except-detail-messages \ 304-method-tracing \ - 407-arrays \ - 410-floats \ - 411-optimizing-arith-mul \ 412-new-array \ - 413-regalloc-regression \ - 414-static-fields \ 416-optimizing-arith-not \ 417-optimizing-arith-div \ - 421-exceptions \ 422-instanceof \ 422-type-conversion \ 423-invoke-interface \ 424-checkcast \ 425-invoke-super \ 426-monitor \ - 427-bounds \ 428-optimizing-arith-rem \ 430-live-register-slow-path \ 431-type-propagation \ @@ -156,9 +122,7 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 434-invoke-direct \ 436-rem-float \ 437-inline \ - 438-volatile \ 439-npe \ - 441-checker-inliner \ 442-checker-constant-folding \ 444-checker-nce \ 445-checker-licm \ @@ -166,83 +130,56 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 448-multiple-returns \ 449-checker-bce \ 450-checker-types \ - 451-regression-add-float \ - 451-spill-splot \ 452-multiple-returns2 \ 453-not-byte \ - 454-get-vreg \ - 456-baseline-array-set \ - 457-regs \ 458-checker-instruct-simplification \ 458-long-to-fpu \ - 459-dead-phi \ 460-multiple-returns3 \ - 461-get-reference-vreg \ 463-checker-boolean-simplifier \ - 466-get-live-vreg \ 467-regalloc-pair \ 468-checker-bool-simplif-regression \ - 469-condition-materialization \ - 471-deopt-environment \ - 472-type-propagation \ - 474-checker-boolean-input \ 475-regression-inliner-ids \ 477-checker-bound-type \ 478-checker-clinit-check-pruning \ 483-dce-block \ - 484-checker-register-hints \ 485-checker-dce-switch \ 486-checker-must-do-null-check \ 488-checker-inline-recursive-calls \ 490-checker-inline \ - 491-current-method \ 492-checker-inline-invoke-interface \ 493-checker-inline-invoke-interface \ 494-checker-instanceof-tests \ 495-checker-checkcast-tests \ 496-checker-inlining-class-loader \ 497-inlining-and-class-loader \ - 498-type-propagation \ - 499-bce-phi-array-length \ 500-instanceof \ - 501-null-constant-dce \ 501-regression-packed-switch \ - 503-dead-instructions \ 504-regression-baseline-entry \ 508-checker-disassembly \ 510-checker-try-catch \ - 513-array-deopt \ 515-dce-dominator \ 517-checker-builder-fallthrough \ 518-null-array-get \ 519-bound-load-class \ 520-equivalent-phi \ - 521-checker-array-set-null \ - 521-regression-integer-field-set \ 522-checker-regression-monitor-exit \ 523-checker-can-throw-regression \ 525-checker-arrays-fields1 \ 525-checker-arrays-fields2 \ 526-checker-caller-callee-regs \ - 526-long-regalloc \ 527-checker-array-access-split \ 528-long-hint \ 529-checker-unresolved \ - 529-long-split \ 530-checker-loops1 \ 530-checker-loops2 \ - 530-checker-loops3 \ 530-checker-lse \ 530-checker-regression-reftyp-final \ 530-instanceof-checkcast \ - 532-checker-nonnull-arrayset \ 534-checker-bce-deoptimization \ - 535-deopt-and-inlining \ 535-regression-const-val \ 536-checker-intrinsic-optimization \ 536-checker-needs-access-check \ 537-checker-inline-and-unverified \ - 537-checker-jump-over-jump \ 538-checker-embed-constants \ 540-checker-rtp-bug \ 541-regression-inlined-deopt \ @@ -253,9 +190,8 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 546-regression-simplify-catch \ 550-checker-multiply-accumulate \ 550-checker-regression-wide-store \ - 551-checker-shifter-operand \ - 551-invoke-super \ 552-checker-sharpening \ + 551-invoke-super \ 552-checker-primitive-typeprop \ 552-invoke-non-existent-super \ 553-invoke-super \ @@ -263,87 +199,51 @@ TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 555-UnsafeGetLong-regression \ 556-invoke-super \ 558-switch \ - 559-bce-ssa \ - 559-checker-irreducible-loop \ - 559-checker-rtp-ifnotnull \ 560-packed-switch \ 561-divrem \ - 561-shared-slowpaths \ 562-bce-preheader \ - 562-no-intermediate \ 563-checker-fakestring \ - 564-checker-irreducible-loop \ 564-checker-negbitwise \ - 565-checker-doublenegbitwise \ - 565-checker-irreducible-loop \ 566-polymorphic-inlining \ - 568-checker-onebit \ 570-checker-osr \ 570-checker-select \ - 571-irreducible-loop \ - 572-checker-array-get-regression \ 573-checker-checkcast-regression \ - 574-irreducible-and-constant-area \ - 575-checker-isnan \ 575-checker-string-init-alias \ - 577-checker-fp2int \ - 578-bce-visit \ 580-checker-round \ - 580-checker-string-fact-intrinsics \ - 581-rtp \ - 582-checker-bce-length \ 584-checker-div-bool \ 586-checker-null-array-get \ 587-inline-class-error \ 588-checker-irreducib-lifetime-hole \ 589-super-imt \ - 590-checker-arr-set-null-regression \ - 591-new-instance-string \ 592-checker-regression-bool-input \ - 593-checker-long-2-float-regression \ - 593-checker-shift-and-simplifier \ 594-checker-array-alias \ 594-invoke-super \ 594-load-string-regression \ - 595-error-class \ - 596-checker-dead-phi \ 597-deopt-new-string \ - 599-checker-irreducible-loop \ 600-verifier-fails \ 601-method-access \ 602-deoptimizeable \ 603-checker-instanceof \ - 604-hot-static-interface \ 605-new-string-from-bytes \ 608-checker-unresolved-lse \ 609-checker-inline-interface \ - 609-checker-x86-bounds-check \ 610-arraycopy \ - 611-checker-simplify-if \ 612-jit-dex-cache \ - 613-inlining-dex-cache \ - 614-checker-dump-constant-location \ - 615-checker-arm64-store-zero \ - 617-clinit-oome \ - 618-checker-induction \ - 621-checker-new-instance \ 700-LoadArgRegs \ 701-easy-div-rem \ 702-LargeBranchOffset \ - 704-multiply-accumulate \ 800-smali \ 802-deoptimization \ 960-default-smali \ - 961-default-iface-resolution-gen \ 963-default-range-smali \ 965-default-verify \ 966-default-conflict \ 967-default-ame \ - 968-default-partial-compile-gen \ 969-iface-super \ 971-iface-super \ 972-default-imt-collision \ 972-iface-super-multidex \ 973-default-multidex \ 974-verify-interface-super \ - 975-iface-private + 975-iface-private \ + 979-invoke-polymorphic-accessors diff --git a/test/Android.bp b/test/Android.bp index af70486abf..bdb7f80eb0 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -256,6 +256,7 @@ art_cc_defaults { "910-methods/methods.cc", "911-get-stack-trace/stack_trace.cc", "912-classes/classes.cc", + "913-heaps/heaps.cc", ], shared_libs: [ "libbase", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 2251b7e163..ae569f91a3 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -279,6 +279,7 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 910-methods \ 911-get-stack-trace \ 912-classes \ + 913-heaps \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -374,13 +375,14 @@ TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := # * 137-cfi needs to unwind a second forked process. We're using a primitive sleep to wait till we # hope the second process got into the expected state. The slowness of gcstress makes this bad. # * 908-gc-start-finish expects GCs only to be run at clear points. The reduced heap size makes -# this non-deterministic. +# this non-deterministic. Same for 913. # * 961-default-iface-resolution-gen and 964-default-iface-init-genare very long tests that often # will take more than the timeout to run when gcstress is enabled. This is because gcstress # slows down allocations significantly which these tests do a lot. TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \ 137-cfi \ 908-gc-start-finish \ + 913-heaps \ 961-default-iface-resolution-gen \ 964-default-iface-init-gen \ diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 3535f323d0..cc0d008221 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -148,7 +148,7 @@ while true; do SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar" # Enable cfg-append to make sure we get the dump for both dex files. # (otherwise the runtime compilation of the secondary dex will overwrite - # the dump of the first one) + # the dump of the first one). FLAGS="${FLAGS} -Xcompiler-option --dump-cfg-append" COMPILE_FLAGS="${COMPILE_FLAGS} --dump-cfg-append" shift diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 4db953cc4a..a959482772 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -36,6 +36,7 @@ #include "910-methods/methods.h" #include "911-get-stack-trace/stack_trace.h" #include "912-classes/classes.h" +#include "913-heaps/heaps.h" namespace art { @@ -64,6 +65,7 @@ AgentLib agents[] = { { "910-methods", Test910Methods::OnLoad, nullptr }, { "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr }, { "912-classes", Test912Classes::OnLoad, nullptr }, + { "913-heaps", Test913Heaps::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { diff --git a/tools/cpp-define-generator/constant_class.def b/tools/cpp-define-generator/constant_class.def index 58372f91d3..f46cd339b0 100644 --- a/tools/cpp-define-generator/constant_class.def +++ b/tools/cpp-define-generator/constant_class.def @@ -25,6 +25,7 @@ DEFINE_FLAG_OFFSET(MIRROR_CLASS, STATUS_INITIALIZED, art::mirror::Class::kStatusInitialized) DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE, art::kAccClassIsFinalizable) +DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_INTERFACE, art::kAccInterface) // TODO: We should really have a BitPosition which also checks it's a power of 2. DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE_BIT, art::MostSignificantBit(art::kAccClassIsFinalizable)) diff --git a/tools/cpp-define-generator/generate-asm-support b/tools/cpp-define-generator/generate-asm-support index f95648b2b7..fcdf72fda9 100755 --- a/tools/cpp-define-generator/generate-asm-support +++ b/tools/cpp-define-generator/generate-asm-support @@ -5,4 +5,4 @@ [[ -z ${ANDROID_BUILD_TOP+x} ]] && (echo "Run source build/envsetup.sh first" >&2 && exit 1) -cpp-define-generator-datad > ${ANDROID_BUILD_TOP}/art/runtime/generated/asm_support_gen.h +cpp-define-generator-data > ${ANDROID_BUILD_TOP}/art/runtime/generated/asm_support_gen.h |