diff options
102 files changed, 3129 insertions, 693 deletions
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc index 174efdf115..6b0ec253e9 100644 --- a/compiler/optimizing/code_generator_vector_arm64.cc +++ b/compiler/optimizing/code_generator_vector_arm64.cc @@ -63,7 +63,7 @@ void LocationsBuilderARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruc } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -125,7 +125,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -149,7 +149,7 @@ void LocationsBuilderARM64::VisitVecExtractScalar(HVecExtractScalar* instruction locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -173,7 +173,7 @@ void InstructionCodeGeneratorARM64::VisitVecExtractScalar(HVecExtractScalar* ins DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -200,7 +200,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -240,7 +240,7 @@ void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -259,7 +259,7 @@ void InstructionCodeGeneratorARM64::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ Scvtf(dst.V4S(), src.V4S()); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -299,7 +299,7 @@ void InstructionCodeGeneratorARM64::VisitVecNeg(HVecNeg* instruction) { __ Fneg(dst.V2D(), src.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -338,7 +338,7 @@ void InstructionCodeGeneratorARM64::VisitVecAbs(HVecAbs* instruction) { __ Fabs(dst.V2D(), src.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -366,7 +366,7 @@ void InstructionCodeGeneratorARM64::VisitVecNot(HVecNot* instruction) { __ Not(dst.V16B(), src.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -389,7 +389,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -431,7 +431,39 @@ void InstructionCodeGeneratorARM64::VisitVecAdd(HVecAdd* instruction) { __ Fadd(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARM64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VRegister lhs = VRegisterFrom(locations->InAt(0)); + VRegister rhs = VRegisterFrom(locations->InAt(1)); + VRegister dst = VRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Uqadd(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Sqadd(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Uqadd(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Sqadd(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -471,7 +503,7 @@ void InstructionCodeGeneratorARM64::VisitVecHalvingAdd(HVecHalvingAdd* instructi : __ Shadd(dst.V8H(), lhs.V8H(), rhs.V8H()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -513,7 +545,39 @@ void InstructionCodeGeneratorARM64::VisitVecSub(HVecSub* instruction) { __ Fsub(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARM64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VRegister lhs = VRegisterFrom(locations->InAt(0)); + VRegister rhs = VRegisterFrom(locations->InAt(1)); + VRegister dst = VRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Uqsub(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ Sqsub(dst.V16B(), lhs.V16B(), rhs.V16B()); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Uqsub(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Sqsub(dst.V8H(), lhs.V8H(), rhs.V8H()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -551,7 +615,7 @@ void InstructionCodeGeneratorARM64::VisitVecMul(HVecMul* instruction) { __ Fmul(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -575,7 +639,7 @@ void InstructionCodeGeneratorARM64::VisitVecDiv(HVecDiv* instruction) { __ Fdiv(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -623,7 +687,7 @@ void InstructionCodeGeneratorARM64::VisitVecMin(HVecMin* instruction) { __ Fmin(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -671,7 +735,7 @@ void InstructionCodeGeneratorARM64::VisitVecMax(HVecMax* instruction) { __ Fmax(dst.V2D(), lhs.V2D(), rhs.V2D()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -699,7 +763,7 @@ void InstructionCodeGeneratorARM64::VisitVecAnd(HVecAnd* instruction) { __ And(dst.V16B(), lhs.V16B(), rhs.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -735,7 +799,7 @@ void InstructionCodeGeneratorARM64::VisitVecOr(HVecOr* instruction) { __ Orr(dst.V16B(), lhs.V16B(), rhs.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -762,7 +826,7 @@ void InstructionCodeGeneratorARM64::VisitVecXor(HVecXor* instruction) { __ Eor(dst.V16B(), lhs.V16B(), rhs.V16B()); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -782,7 +846,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -816,7 +880,7 @@ void InstructionCodeGeneratorARM64::VisitVecShl(HVecShl* instruction) { __ Shl(dst.V2D(), lhs.V2D(), value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -850,7 +914,7 @@ void InstructionCodeGeneratorARM64::VisitVecShr(HVecShr* instruction) { __ Sshr(dst.V2D(), lhs.V2D(), value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -884,7 +948,7 @@ void InstructionCodeGeneratorARM64::VisitVecUShr(HVecUShr* instruction) { __ Ushr(dst.V2D(), lhs.V2D(), value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -916,7 +980,7 @@ void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -957,7 +1021,7 @@ void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instructi __ Mov(dst.V2D(), 0, InputRegisterAt(instruction, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -978,7 +1042,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1026,7 +1090,7 @@ void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccum } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1139,7 +1203,7 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1167,7 +1231,7 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1188,7 +1252,7 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins __ Sabal2(acc.V2D(), left.V4S(), right.V4S()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1204,12 +1268,12 @@ void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* ins break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -1237,7 +1301,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1331,7 +1395,7 @@ void InstructionCodeGeneratorARM64::VisitVecLoad(HVecLoad* instruction) { __ Ldr(reg, VecAddress(instruction, &temps, size, instruction->IsStringCharAt(), &scratch)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1362,7 +1426,7 @@ void InstructionCodeGeneratorARM64::VisitVecStore(HVecStore* instruction) { __ Str(reg, VecAddress(instruction, &temps, size, /*is_string_char_at*/ false, &scratch)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc index 7c3155ab73..7b66b17983 100644 --- a/compiler/optimizing/code_generator_vector_arm_vixl.cc +++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc @@ -46,7 +46,7 @@ void LocationsBuilderARMVIXL::VisitVecReplicateScalar(HVecReplicateScalar* instr locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -71,7 +71,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecReplicateScalar(HVecReplicateScala __ Vdup(Untyped32, dst, InputRegisterAt(instruction, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -84,7 +84,7 @@ void LocationsBuilderARMVIXL::VisitVecExtractScalar(HVecExtractScalar* instructi locations->SetOut(Location::RequiresRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -98,7 +98,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecExtractScalar(HVecExtractScalar* i __ Vmov(OutputRegister(instruction), DRegisterLane(src, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -122,7 +122,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -151,7 +151,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -188,7 +188,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecNeg(HVecNeg* instruction) { __ Vneg(DataTypeValue::S32, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -215,7 +215,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecAbs(HVecAbs* instruction) { __ Vabs(DataTypeValue::S32, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -242,7 +242,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecNot(HVecNot* instruction) { __ Vmvn(I8, dst, src); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -262,7 +262,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -292,7 +292,39 @@ void InstructionCodeGeneratorARMVIXL::VisitVecAdd(HVecAdd* instruction) { __ Vadd(I32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARMVIXL::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARMVIXL::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0)); + vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1)); + vixl32::DRegister dst = DRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::U8, dst, lhs, rhs); + break; + case DataType::Type::kInt8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::S8, dst, lhs, rhs); + break; + case DataType::Type::kUint16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::U16, dst, lhs, rhs); + break; + case DataType::Type::kInt16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqadd(DataTypeValue::S16, dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -332,7 +364,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecHalvingAdd(HVecHalvingAdd* instruc : __ Vhadd(DataTypeValue::S16, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -362,7 +394,39 @@ void InstructionCodeGeneratorARMVIXL::VisitVecSub(HVecSub* instruction) { __ Vsub(I32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderARMVIXL::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorARMVIXL::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0)); + vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1)); + vixl32::DRegister dst = DRegisterFrom(locations->Out()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::U8, dst, lhs, rhs); + break; + case DataType::Type::kInt8: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::S8, dst, lhs, rhs); + break; + case DataType::Type::kUint16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::U16, dst, lhs, rhs); + break; + case DataType::Type::kInt16: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Vqsub(DataTypeValue::S16, dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -392,7 +456,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMul(HVecMul* instruction) { __ Vmul(I32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -440,7 +504,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMin(HVecMin* instruction) { __ Vmin(DataTypeValue::S32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -480,7 +544,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMax(HVecMax* instruction) { __ Vmax(DataTypeValue::S32, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -505,7 +569,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecAnd(HVecAnd* instruction) { __ Vand(I8, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -537,7 +601,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecOr(HVecOr* instruction) { __ Vorr(I8, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -561,7 +625,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecXor(HVecXor* instruction) { __ Veor(I8, dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -580,7 +644,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -610,7 +674,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecShl(HVecShl* instruction) { __ Vshl(I32, dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -640,7 +704,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecShr(HVecShr* instruction) { __ Vshr(DataTypeValue::S32, dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -670,7 +734,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecUShr(HVecUShr* instruction) { __ Vshr(DataTypeValue::U32, dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -690,7 +754,7 @@ void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -716,7 +780,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruc __ Vmov(Untyped32, DRegisterLane(dst, 0), InputRegisterAt(instruction, 0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -737,7 +801,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -780,12 +844,12 @@ void InstructionCodeGeneratorARMVIXL::VisitVecSADAccumulate(HVecSADAccumulate* i break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -817,7 +881,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -923,7 +987,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecLoad(HVecLoad* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -971,7 +1035,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecStore(HVecStore* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc index ed9de96496..df0e1485d6 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -42,7 +42,7 @@ void LocationsBuilderMIPS::VisitVecReplicateScalar(HVecReplicateScalar* instruct locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -89,7 +89,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReplicateScalar(HVecReplicateScalar* /* is_double */ true); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -113,7 +113,7 @@ void LocationsBuilderMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -138,7 +138,7 @@ void InstructionCodeGeneratorMIPS::VisitVecExtractScalar(HVecExtractScalar* inst DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -170,7 +170,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation : Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -225,7 +225,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -244,7 +244,7 @@ void InstructionCodeGeneratorMIPS::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ Ffint_sW(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -290,7 +290,7 @@ void InstructionCodeGeneratorMIPS::VisitVecNeg(HVecNeg* instruction) { __ FsubD(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -337,7 +337,7 @@ void InstructionCodeGeneratorMIPS::VisitVecAbs(HVecAbs* instruction) { __ AndV(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -369,7 +369,7 @@ void InstructionCodeGeneratorMIPS::VisitVecNot(HVecNot* instruction) { __ NorV(dst, src, src); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -392,7 +392,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -434,11 +434,19 @@ void InstructionCodeGeneratorMIPS::VisitVecAdd(HVecAdd* instruction) { __ FaddD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -474,7 +482,7 @@ void InstructionCodeGeneratorMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instructio : __ Ave_sH(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -516,11 +524,19 @@ void InstructionCodeGeneratorMIPS::VisitVecSub(HVecSub* instruction) { __ FsubD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS::VisitVecMul(HVecMul* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -558,7 +574,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMul(HVecMul* instruction) { __ FmulD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -582,7 +598,7 @@ void InstructionCodeGeneratorMIPS::VisitVecDiv(HVecDiv* instruction) { __ FdivD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -640,7 +656,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMin(HVecMin* instruction) { __ FminD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -698,7 +714,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMax(HVecMax* instruction) { __ FmaxD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -727,7 +743,7 @@ void InstructionCodeGeneratorMIPS::VisitVecAnd(HVecAnd* instruction) { __ AndV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -764,7 +780,7 @@ void InstructionCodeGeneratorMIPS::VisitVecOr(HVecOr* instruction) { __ OrV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -793,7 +809,7 @@ void InstructionCodeGeneratorMIPS::VisitVecXor(HVecXor* instruction) { __ XorV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -813,7 +829,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -847,7 +863,7 @@ void InstructionCodeGeneratorMIPS::VisitVecShl(HVecShl* instruction) { __ SlliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -881,7 +897,7 @@ void InstructionCodeGeneratorMIPS::VisitVecShr(HVecShr* instruction) { __ SraiD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -915,7 +931,7 @@ void InstructionCodeGeneratorMIPS::VisitVecUShr(HVecUShr* instruction) { __ SrliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -947,7 +963,7 @@ void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -989,7 +1005,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instructio __ InsertW(dst, locations->InAt(0).AsRegisterPairHigh<Register>(), 1); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1010,7 +1026,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1060,7 +1076,7 @@ void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumu } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1162,7 +1178,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1201,7 +1217,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1231,7 +1247,7 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1247,13 +1263,13 @@ void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* inst break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1282,7 +1298,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1357,7 +1373,7 @@ void InstructionCodeGeneratorMIPS::VisitVecLoad(HVecLoad* instruction) { __ LdD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1395,7 +1411,7 @@ void InstructionCodeGeneratorMIPS::VisitVecStore(HVecStore* instruction) { __ StD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc index 9ea55ec8d7..de354b63a1 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -47,7 +47,7 @@ void LocationsBuilderMIPS64::VisitVecReplicateScalar(HVecReplicateScalar* instru locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -88,7 +88,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReplicateScalar(HVecReplicateScalar /* is_double */ true); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -112,7 +112,7 @@ void LocationsBuilderMIPS64::VisitVecExtractScalar(HVecExtractScalar* instructio locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -136,7 +136,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecExtractScalar(HVecExtractScalar* in DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -168,7 +168,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation : Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -223,7 +223,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) { } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -242,7 +242,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ Ffint_sW(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -289,7 +289,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecNeg(HVecNeg* instruction) { __ FsubD(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -336,7 +336,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecAbs(HVecAbs* instruction) { __ AndV(dst, dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -368,7 +368,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecNot(HVecNot* instruction) { __ NorV(dst, src, src); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -391,7 +391,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -433,11 +433,19 @@ void InstructionCodeGeneratorMIPS64::VisitVecAdd(HVecAdd* instruction) { __ FaddD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -473,7 +481,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecHalvingAdd(HVecHalvingAdd* instruct : __ Ave_sH(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -515,11 +523,19 @@ void InstructionCodeGeneratorMIPS64::VisitVecSub(HVecSub* instruction) { __ FsubD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } +void LocationsBuilderMIPS64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LOG(FATAL) << "Unsupported SIMD " << instruction->GetId(); +} + void LocationsBuilderMIPS64::VisitVecMul(HVecMul* instruction) { CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); } @@ -557,7 +573,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMul(HVecMul* instruction) { __ FmulD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -581,7 +597,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecDiv(HVecDiv* instruction) { __ FdivD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -639,7 +655,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMin(HVecMin* instruction) { __ FminD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -697,7 +713,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMax(HVecMax* instruction) { __ FmaxD(dst, lhs, rhs); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -726,7 +742,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecAnd(HVecAnd* instruction) { __ AndV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -763,7 +779,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecOr(HVecOr* instruction) { __ OrV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -792,7 +808,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecXor(HVecXor* instruction) { __ XorV(dst, lhs, rhs); // lanes do not matter break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -812,7 +828,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -846,7 +862,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecShl(HVecShl* instruction) { __ SlliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -880,7 +896,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecShr(HVecShr* instruction) { __ SraiD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -914,7 +930,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecUShr(HVecUShr* instruction) { __ SrliD(dst, lhs, value); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -946,7 +962,7 @@ void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -987,7 +1003,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruct __ InsertD(dst, locations->InAt(0).AsRegister<GpuRegister>(), 0); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1008,7 +1024,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1058,7 +1074,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccu } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1160,7 +1176,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1199,7 +1215,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1229,7 +1245,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; @@ -1245,13 +1261,13 @@ void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* in break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1280,7 +1296,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1355,7 +1371,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecLoad(HVecLoad* instruction) { __ LdD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1393,7 +1409,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecStore(HVecStore* instruction) { __ StD(reg, base, offset); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc index f2ffccc887..086ae07a06 100644 --- a/compiler/optimizing/code_generator_vector_x86.cc +++ b/compiler/optimizing/code_generator_vector_x86.cc @@ -54,7 +54,7 @@ void LocationsBuilderX86::VisitVecReplicateScalar(HVecReplicateScalar* instructi : Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -111,7 +111,7 @@ void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* i __ shufpd(dst, dst, Immediate(0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -138,7 +138,7 @@ void LocationsBuilderX86::VisitVecExtractScalar(HVecExtractScalar* instruction) locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -152,7 +152,7 @@ void InstructionCodeGeneratorX86::VisitVecExtractScalar(HVecExtractScalar* instr case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_LE(4u, instruction->GetVectorLength()); @@ -174,7 +174,7 @@ void InstructionCodeGeneratorX86::VisitVecExtractScalar(HVecExtractScalar* instr DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -196,7 +196,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -258,12 +258,12 @@ void InstructionCodeGeneratorX86::VisitVecReduce(HVecReduce* instruction) { break; case HVecReduce::kMin: case HVecReduce::kMax: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -282,7 +282,7 @@ void InstructionCodeGeneratorX86::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ cvtdq2ps(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -328,7 +328,7 @@ void InstructionCodeGeneratorX86::VisitVecNeg(HVecNeg* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -369,7 +369,7 @@ void InstructionCodeGeneratorX86::VisitVecAbs(HVecAbs* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -418,7 +418,7 @@ void InstructionCodeGeneratorX86::VisitVecNot(HVecNot* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -441,7 +441,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -483,7 +483,39 @@ void InstructionCodeGeneratorX86::VisitVecAdd(HVecAdd* instruction) { __ addpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>(); + XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -503,14 +535,14 @@ void InstructionCodeGeneratorX86::VisitVecHalvingAdd(HVecHalvingAdd* instruction switch (instruction->GetPackedType()) { case DataType::Type::kUint8: DCHECK_EQ(16u, instruction->GetVectorLength()); - __ pavgb(dst, src); - return; + __ pavgb(dst, src); + break; case DataType::Type::kUint16: DCHECK_EQ(8u, instruction->GetVectorLength()); __ pavgw(dst, src); - return; + break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -552,7 +584,39 @@ void InstructionCodeGeneratorX86::VisitVecSub(HVecSub* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>(); + XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -585,7 +649,7 @@ void InstructionCodeGeneratorX86::VisitVecMul(HVecMul* instruction) { __ mulpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -609,7 +673,7 @@ void InstructionCodeGeneratorX86::VisitVecDiv(HVecDiv* instruction) { __ divpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -658,7 +722,7 @@ void InstructionCodeGeneratorX86::VisitVecMin(HVecMin* instruction) { __ minpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -707,7 +771,7 @@ void InstructionCodeGeneratorX86::VisitVecMax(HVecMax* instruction) { __ maxpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -742,7 +806,7 @@ void InstructionCodeGeneratorX86::VisitVecAnd(HVecAnd* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -777,7 +841,7 @@ void InstructionCodeGeneratorX86::VisitVecAndNot(HVecAndNot* instruction) { __ andnpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -812,7 +876,7 @@ void InstructionCodeGeneratorX86::VisitVecOr(HVecOr* instruction) { __ orpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -847,7 +911,7 @@ void InstructionCodeGeneratorX86::VisitVecXor(HVecXor* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -865,7 +929,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -894,7 +958,7 @@ void InstructionCodeGeneratorX86::VisitVecShl(HVecShl* instruction) { __ psllq(dst, Immediate(static_cast<uint8_t>(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -919,7 +983,7 @@ void InstructionCodeGeneratorX86::VisitVecShr(HVecShr* instruction) { __ psrad(dst, Immediate(static_cast<uint8_t>(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -948,7 +1012,7 @@ void InstructionCodeGeneratorX86::VisitVecUShr(HVecUShr* instruction) { __ psrlq(dst, Immediate(static_cast<uint8_t>(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -985,7 +1049,7 @@ void LocationsBuilderX86::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1011,7 +1075,7 @@ void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); @@ -1035,7 +1099,7 @@ void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction __ movsd(dst, locations->InAt(1).AsFpuRegister<XmmRegister>()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1056,7 +1120,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1103,7 +1167,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1184,7 +1248,7 @@ void InstructionCodeGeneratorX86::VisitVecLoad(HVecLoad* instruction) { is_aligned16 ? __ movapd(reg, address) : __ movupd(reg, address); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1220,7 +1284,7 @@ void InstructionCodeGeneratorX86::VisitVecStore(HVecStore* instruction) { is_aligned16 ? __ movapd(address, reg) : __ movupd(address, reg); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc index e2b0485f89..4d31ab68d1 100644 --- a/compiler/optimizing/code_generator_vector_x86_64.cc +++ b/compiler/optimizing/code_generator_vector_x86_64.cc @@ -49,7 +49,7 @@ void LocationsBuilderX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instru : Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -102,7 +102,7 @@ void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar __ shufpd(dst, dst, Immediate(0)); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -126,7 +126,7 @@ void LocationsBuilderX86_64::VisitVecExtractScalar(HVecExtractScalar* instructio locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -140,7 +140,7 @@ void InstructionCodeGeneratorX86_64::VisitVecExtractScalar(HVecExtractScalar* in case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); @@ -157,7 +157,7 @@ void InstructionCodeGeneratorX86_64::VisitVecExtractScalar(HVecExtractScalar* in DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -179,7 +179,7 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -241,12 +241,12 @@ void InstructionCodeGeneratorX86_64::VisitVecReduce(HVecReduce* instruction) { break; case HVecReduce::kMin: case HVecReduce::kMax: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } break; } default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -265,7 +265,7 @@ void InstructionCodeGeneratorX86_64::VisitVecCnv(HVecCnv* instruction) { DCHECK_EQ(4u, instruction->GetVectorLength()); __ cvtdq2ps(dst, src); } else { - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); } } @@ -311,7 +311,7 @@ void InstructionCodeGeneratorX86_64::VisitVecNeg(HVecNeg* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -352,7 +352,7 @@ void InstructionCodeGeneratorX86_64::VisitVecAbs(HVecAbs* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -401,7 +401,7 @@ void InstructionCodeGeneratorX86_64::VisitVecNot(HVecNot* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -424,7 +424,7 @@ static void CreateVecBinOpLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -466,7 +466,39 @@ void InstructionCodeGeneratorX86_64::VisitVecAdd(HVecAdd* instruction) { __ addpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86_64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86_64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>(); + XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ paddsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ paddsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -486,14 +518,14 @@ void InstructionCodeGeneratorX86_64::VisitVecHalvingAdd(HVecHalvingAdd* instruct switch (instruction->GetPackedType()) { case DataType::Type::kUint8: DCHECK_EQ(16u, instruction->GetVectorLength()); - __ pavgb(dst, src); - return; + __ pavgb(dst, src); + break; case DataType::Type::kUint16: DCHECK_EQ(8u, instruction->GetVectorLength()); __ pavgw(dst, src); - return; + break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -535,7 +567,39 @@ void InstructionCodeGeneratorX86_64::VisitVecSub(HVecSub* instruction) { __ subpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); + UNREACHABLE(); + } +} + +void LocationsBuilderX86_64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + CreateVecBinOpLocations(GetGraph()->GetAllocator(), instruction); +} + +void InstructionCodeGeneratorX86_64::VisitVecSaturationSub(HVecSaturationSub* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK(locations->InAt(0).Equals(locations->Out())); + XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>(); + XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>(); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubusb(dst, src); + break; + case DataType::Type::kInt8: + DCHECK_EQ(16u, instruction->GetVectorLength()); + __ psubsb(dst, src); + break; + case DataType::Type::kUint16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubusw(dst, src); + break; + case DataType::Type::kInt16: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ psubsw(dst, src); + break; + default: + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -568,7 +632,7 @@ void InstructionCodeGeneratorX86_64::VisitVecMul(HVecMul* instruction) { __ mulpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -592,7 +656,7 @@ void InstructionCodeGeneratorX86_64::VisitVecDiv(HVecDiv* instruction) { __ divpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -641,7 +705,7 @@ void InstructionCodeGeneratorX86_64::VisitVecMin(HVecMin* instruction) { __ minpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -690,7 +754,7 @@ void InstructionCodeGeneratorX86_64::VisitVecMax(HVecMax* instruction) { __ maxpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -725,7 +789,7 @@ void InstructionCodeGeneratorX86_64::VisitVecAnd(HVecAnd* instruction) { __ andpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -760,7 +824,7 @@ void InstructionCodeGeneratorX86_64::VisitVecAndNot(HVecAndNot* instruction) { __ andnpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -795,7 +859,7 @@ void InstructionCodeGeneratorX86_64::VisitVecOr(HVecOr* instruction) { __ orpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -830,7 +894,7 @@ void InstructionCodeGeneratorX86_64::VisitVecXor(HVecXor* instruction) { __ xorpd(dst, src); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -848,7 +912,7 @@ static void CreateVecShiftLocations(ArenaAllocator* allocator, HVecBinaryOperati locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -877,7 +941,7 @@ void InstructionCodeGeneratorX86_64::VisitVecShl(HVecShl* instruction) { __ psllq(dst, Immediate(static_cast<int8_t>(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -902,7 +966,7 @@ void InstructionCodeGeneratorX86_64::VisitVecShr(HVecShr* instruction) { __ psrad(dst, Immediate(static_cast<int8_t>(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -931,7 +995,7 @@ void InstructionCodeGeneratorX86_64::VisitVecUShr(HVecUShr* instruction) { __ psrlq(dst, Immediate(static_cast<int8_t>(value))); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -963,7 +1027,7 @@ void LocationsBuilderX86_64::VisitVecSetScalars(HVecSetScalars* instruction) { locations->SetOut(Location::RequiresFpuRegister()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -989,7 +1053,7 @@ void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruct case DataType::Type::kInt8: case DataType::Type::kUint16: case DataType::Type::kInt16: // TODO: up to here, and? - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); @@ -1008,7 +1072,7 @@ void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruct __ movsd(dst, locations->InAt(0).AsFpuRegister<XmmRegister>()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1029,7 +1093,7 @@ static void CreateVecAccumLocations(ArenaAllocator* allocator, HVecOperation* in locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1076,7 +1140,7 @@ static void CreateVecMemLocations(ArenaAllocator* allocator, } break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1157,7 +1221,7 @@ void InstructionCodeGeneratorX86_64::VisitVecLoad(HVecLoad* instruction) { is_aligned16 ? __ movapd(reg, address) : __ movupd(reg, address); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } @@ -1193,7 +1257,7 @@ void InstructionCodeGeneratorX86_64::VisitVecStore(HVecStore* instruction) { is_aligned16 ? __ movapd(address, reg) : __ movupd(address, reg); break; default: - LOG(FATAL) << "Unsupported SIMD type"; + LOG(FATAL) << "Unsupported SIMD type: " << instruction->GetPackedType(); UNREACHABLE(); } } diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 34837700a2..2b6f90540f 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -854,11 +854,29 @@ static HInstruction* NewIntegralAbs(ArenaAllocator* allocator, HInstruction* cursor) { DataType::Type type = x->GetType(); DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); - HAbs* abs = new (allocator) HAbs(type, x, x->GetDexPc()); + HAbs* abs = new (allocator) HAbs(type, x, cursor->GetDexPc()); cursor->GetBlock()->InsertInstructionBefore(abs, cursor); return abs; } +// Constructs a new MIN/MAX(x, y) node in the HIR. +static HInstruction* NewIntegralMinMax(ArenaAllocator* allocator, + HInstruction* x, + HInstruction* y, + HInstruction* cursor, + bool is_min) { + DataType::Type type = x->GetType(); + DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); + HBinaryOperation* minmax = nullptr; + if (is_min) { + minmax = new (allocator) HMin(type, x, y, cursor->GetDexPc()); + } else { + minmax = new (allocator) HMax(type, x, y, cursor->GetDexPc()); + } + cursor->GetBlock()->InsertInstructionBefore(minmax, cursor); + return minmax; +} + // Returns true if operands a and b consists of widening type conversions // (either explicit or implicit) to the given to_type. static bool AreLowerPrecisionArgs(DataType::Type to_type, HInstruction* a, HInstruction* b) { @@ -924,8 +942,15 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { // Test if both values are same-typed int or long. if (t_type == f_type && (t_type == DataType::Type::kInt32 || t_type == DataType::Type::kInt64)) { - // Try to replace typical integral ABS constructs. - if (true_value->IsNeg()) { + // Try to replace typical integral MIN/MAX/ABS constructs. + if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) && + ((a == true_value && b == false_value) || + (b == true_value && a == false_value))) { + // Found a < b ? a : b (MIN) or a < b ? b : a (MAX) + // or a > b ? a : b (MAX) or a > b ? b : a (MIN). + bool is_min = (cmp == kCondLT || cmp == kCondLE) == (a == true_value); + replace_with = NewIntegralMinMax(GetGraph()->GetAllocator(), a, b, select, is_min); + } else if (true_value->IsNeg()) { HInstruction* negated = true_value->InputAt(0); if ((cmp == kCondLT || cmp == kCondLE) && (a == negated && a == false_value && IsInt64Value(b, 0))) { diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index d3b081e005..e1fb7ac17e 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -331,6 +331,69 @@ static bool IsAddConst(HInstruction* instruction, return false; } +// Detect clipped [lo, hi] range for nested MIN-MAX operations on a clippee, +// such as MIN(hi, MAX(lo, clippee)) for an arbitrary clippee expression. +// Example: MIN(10, MIN(20, MAX(0, x))) yields [0, 10] with clippee x. +static HInstruction* FindClippee(HInstruction* instruction, + /*out*/ int64_t* lo, + /*out*/ int64_t* hi) { + // Iterate into MIN(.., c)-MAX(.., c) expressions and 'tighten' the range [lo, hi]. + while (instruction->IsMin() || instruction->IsMax()) { + HBinaryOperation* min_max = instruction->AsBinaryOperation(); + DCHECK(min_max->GetType() == DataType::Type::kInt32 || + min_max->GetType() == DataType::Type::kInt64); + // Process the constant. + HConstant* right = min_max->GetConstantRight(); + if (right == nullptr) { + break; + } else if (instruction->IsMin()) { + *hi = std::min(*hi, Int64FromConstant(right)); + } else { + *lo = std::max(*lo, Int64FromConstant(right)); + } + instruction = min_max->GetLeastConstantLeft(); + } + // Iteration ends in any other expression (possibly MIN/MAX without constant). + // This leaf expression is the clippee with range [lo, hi]. + return instruction; +} + +// Accept various saturated addition forms. +static bool IsSaturatedAdd(DataType::Type type, int64_t lo, int64_t hi, bool is_unsigned) { + // MIN(r + s, 255) => SAT_ADD_unsigned + // MAX(MIN(r + s, 127), -128) => SAT_ADD_signed etc. + if (DataType::Size(type) == 1) { + return is_unsigned + ? (lo <= 0 && hi == std::numeric_limits<uint8_t>::max()) + : (lo == std::numeric_limits<int8_t>::min() && + hi == std::numeric_limits<int8_t>::max()); + } else if (DataType::Size(type) == 2) { + return is_unsigned + ? (lo <= 0 && hi == std::numeric_limits<uint16_t>::max()) + : (lo == std::numeric_limits<int16_t>::min() && + hi == std::numeric_limits<int16_t>::max()); + } + return false; +} + +// Accept various saturated subtraction forms. +static bool IsSaturatedSub(DataType::Type type, int64_t lo, int64_t hi, bool is_unsigned) { + // MAX(r - s, 0) => SAT_SUB_unsigned + // MIN(MAX(r - s, -128), 127) => SAT_ADD_signed etc. + if (DataType::Size(type) == 1) { + return is_unsigned + ? (lo == 0 && hi >= std::numeric_limits<uint8_t>::max()) + : (lo == std::numeric_limits<int8_t>::min() && + hi == std::numeric_limits<int8_t>::max()); + } else if (DataType::Size(type) == 2) { + return is_unsigned + ? (lo == 0 && hi >= std::numeric_limits<uint16_t>::min()) + : (lo == std::numeric_limits<int16_t>::min() && + hi == std::numeric_limits<int16_t>::max()); + } + return false; +} + // Detect reductions of the following forms, // x = x_phi + .. // x = x_phi - .. @@ -1109,7 +1172,6 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node, return !IsUsedOutsideLoop(node->loop_info, instruction) && !instruction->DoesAnyWrite(); } -// TODO: saturation arithmetic. bool HLoopOptimization::VectorizeUse(LoopNode* node, HInstruction* instruction, bool generate_code, @@ -1308,6 +1370,10 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return true; } } else if (instruction->IsMin() || instruction->IsMax()) { + // Recognize saturation arithmetic. + if (VectorizeSaturationIdiom(node, instruction, generate_code, type, restrictions)) { + return true; + } // Deal with vector restrictions. HInstruction* opa = instruction->InputAt(0); HInstruction* opb = instruction->InputAt(1); @@ -1439,11 +1505,11 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv; + *restrictions |= kNoDiv | kNoSaturation; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoStringCharAt; + *restrictions |= kNoDiv | kNoSaturation | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: *restrictions |= kNoDiv; @@ -1468,11 +1534,11 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv; + *restrictions |= kNoDiv | kNoSaturation; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoStringCharAt; + *restrictions |= kNoDiv | kNoSaturation | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: *restrictions |= kNoDiv; @@ -1811,6 +1877,73 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, // Vectorization idioms. // +// Method recognizes single and double clipping saturation arithmetic. +bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node, + HInstruction* instruction, + bool generate_code, + DataType::Type type, + uint64_t restrictions) { + // Deal with vector restrictions. + if (HasVectorRestrictions(restrictions, kNoSaturation)) { + return false; + } + // Restrict type (generalize if one day we generalize allowed MIN/MAX integral types). + if (instruction->GetType() != DataType::Type::kInt32 && + instruction->GetType() != DataType::Type::kInt64) { + return false; + } + // Clipped addition or subtraction? + int64_t lo = std::numeric_limits<int64_t>::min(); + int64_t hi = std::numeric_limits<int64_t>::max(); + HInstruction* clippee = FindClippee(instruction, &lo, &hi); + bool is_add = true; + if (clippee->IsAdd()) { + is_add = true; + } else if (clippee->IsSub()) { + is_add = false; + } else { + return false; // clippee is not add/sub + } + // Addition or subtraction on narrower operands? + HInstruction* r = nullptr; + HInstruction* s = nullptr; + bool is_unsigned = false; + if (IsNarrowerOperands(clippee->InputAt(0), clippee->InputAt(1), type, &r, &s, &is_unsigned) && + (is_add ? IsSaturatedAdd(type, lo, hi, is_unsigned) + : IsSaturatedSub(type, lo, hi, is_unsigned))) { + DCHECK(r != nullptr); + DCHECK(s != nullptr); + } else { + return false; + } + // Accept saturation idiom for vectorizable operands. + if (generate_code && vector_mode_ != kVector) { // de-idiom + r = instruction->InputAt(0); + s = instruction->InputAt(1); + restrictions &= ~(kNoHiBits | kNoMinMax); // allow narrow MIN/MAX in seq + } + if (VectorizeUse(node, r, generate_code, type, restrictions) && + VectorizeUse(node, s, generate_code, type, restrictions)) { + if (generate_code) { + if (vector_mode_ == kVector) { + DataType::Type vtype = HVecOperation::ToProperType(type, is_unsigned); + HInstruction* op1 = vector_map_->Get(r); + HInstruction* op2 = vector_map_->Get(s); + vector_map_->Put(instruction, is_add + ? reinterpret_cast<HInstruction*>(new (global_allocator_) HVecSaturationAdd( + global_allocator_, op1, op2, vtype, vector_length_, kNoDexPc)) + : reinterpret_cast<HInstruction*>(new (global_allocator_) HVecSaturationSub( + global_allocator_, op1, op2, vtype, vector_length_, kNoDexPc))); + MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom); + } else { + GenerateVecOp(instruction, vector_map_->Get(r), vector_map_->Get(s), type); + } + } + return true; + } + return false; +} + // Method recognizes the following idioms: // rounding halving add (a + b + 1) >> 1 for unsigned/signed operands a, b // truncated halving add (a + b) >> 1 for unsigned/signed operands a, b diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index a707ad1358..9414e5a0c6 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -80,6 +80,7 @@ class HLoopOptimization : public HOptimization { kNoReduction = 1 << 10, // no reduction kNoSAD = 1 << 11, // no sum of absolute differences (SAD) kNoWideSAD = 1 << 12, // no sum of absolute differences (SAD) with operand widening + kNoSaturation = 1 << 13, // no saturation arithmetic }; /* @@ -177,6 +178,11 @@ class HLoopOptimization : public HOptimization { bool is_unsigned = false); // Vectorization idioms. + bool VectorizeSaturationIdiom(LoopNode* node, + HInstruction* instruction, + bool generate_code, + DataType::Type type, + uint64_t restrictions); bool VectorizeHalvingAddIdiom(LoopNode* node, HInstruction* instruction, bool generate_code, diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a8364e0680..cbf748d4fd 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1440,6 +1440,8 @@ class HLoopInformationOutwardIterator : public ValueObject { M(VecAndNot, VecBinaryOperation) \ M(VecOr, VecBinaryOperation) \ M(VecXor, VecBinaryOperation) \ + M(VecSaturationAdd, VecBinaryOperation) \ + M(VecSaturationSub, VecBinaryOperation) \ M(VecShl, VecBinaryOperation) \ M(VecShr, VecBinaryOperation) \ M(VecUShr, VecBinaryOperation) \ diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 0d38d57375..9b114eb1f7 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -325,7 +325,7 @@ class HVecReplicateScalar FINAL : public HVecUnaryOperation { uint32_t dex_pc) : HVecUnaryOperation( kVecReplicateScalar, allocator, scalar, packed_type, vector_length, dex_pc) { - DCHECK(!scalar->IsVecOperation()); + DCHECK(!ReturnsSIMDValue(scalar)); } // A replicate needs to stay in place, since SIMD registers are not @@ -530,6 +530,31 @@ class HVecAdd FINAL : public HVecBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(VecAdd); }; +// Adds every component in the two vectors using saturation arithmetic, +// viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 +_sat y1, .. , xn +_sat yn ] +// for either both signed or both unsigned operands x, y (reflected in packed_type). +class HVecSaturationAdd FINAL : public HVecBinaryOperation { + public: + HVecSaturationAdd(ArenaAllocator* allocator, + HInstruction* left, + HInstruction* right, + DataType::Type packed_type, + size_t vector_length, + uint32_t dex_pc) + : HVecBinaryOperation( + kVecSaturationAdd, allocator, left, right, packed_type, vector_length, dex_pc) { + DCHECK(HasConsistentPackedTypes(left, packed_type)); + DCHECK(HasConsistentPackedTypes(right, packed_type)); + } + + bool CanBeMoved() const OVERRIDE { return true; } + + DECLARE_INSTRUCTION(VecSaturationAdd); + + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSaturationAdd); +}; + // Performs halving add on every component in the two vectors, viz. // rounded [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ] // truncated [ x1, .. , xn ] hadd [ y1, .. , yn ] = [ (x1 + y1) >> 1, .. , (xn + yn ) >> 1 ] @@ -595,6 +620,31 @@ class HVecSub FINAL : public HVecBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(VecSub); }; +// Subtracts every component in the two vectors using saturation arithmetic, +// viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 -_sat y1, .. , xn -_sat yn ] +// for either both signed or both unsigned operands x, y (reflected in packed_type). +class HVecSaturationSub FINAL : public HVecBinaryOperation { + public: + HVecSaturationSub(ArenaAllocator* allocator, + HInstruction* left, + HInstruction* right, + DataType::Type packed_type, + size_t vector_length, + uint32_t dex_pc) + : HVecBinaryOperation( + kVecSaturationSub, allocator, left, right, packed_type, vector_length, dex_pc) { + DCHECK(HasConsistentPackedTypes(left, packed_type)); + DCHECK(HasConsistentPackedTypes(right, packed_type)); + } + + bool CanBeMoved() const OVERRIDE { return true; } + + DECLARE_INSTRUCTION(VecSaturationSub); + + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSaturationSub); +}; + // Multiplies every component in the two vectors, // viz. [ x1, .. , xn ] * [ y1, .. , yn ] = [ x1 * y1, .. , xn * yn ]. class HVecMul FINAL : public HVecBinaryOperation { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 094dfee3a6..09ff14e4ba 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1465,14 +1465,14 @@ TEST_F(Dex2oatTest, LayoutSections) { // Test that generating compact dex works. TEST_F(Dex2oatTest, GenerateCompactDex) { - std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods")); // Generate a compact dex based odex. const std::string dir = GetScratchDir(); const std::string oat_filename = dir + "/base.oat"; const std::string vdex_filename = dir + "/base.vdex"; + const std::string dex_location = GetTestDexFileName("MultiDex"); std::string error_msg; const int res = GenerateOdexForTestWithStatus( - {dex->GetLocation()}, + { dex_location }, oat_filename, CompilerFilter::Filter::kQuicken, &error_msg, @@ -1485,16 +1485,43 @@ TEST_F(Dex2oatTest, GenerateCompactDex) { nullptr, false, /*low_4gb*/false, - dex->GetLocation().c_str(), + dex_location.c_str(), &error_msg)); ASSERT_TRUE(odex_file != nullptr); std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); - ASSERT_EQ(oat_dex_files.size(), 1u); - // Check that each dex is a compact dex. + ASSERT_GT(oat_dex_files.size(), 1u); + // Check that each dex is a compact dex file. + std::vector<std::unique_ptr<const CompactDexFile>> compact_dex_files; for (const OatDexFile* oat_dex : oat_dex_files) { std::unique_ptr<const DexFile> dex_file(oat_dex->OpenDexFile(&error_msg)); ASSERT_TRUE(dex_file != nullptr) << error_msg; ASSERT_TRUE(dex_file->IsCompactDexFile()); + compact_dex_files.push_back( + std::unique_ptr<const CompactDexFile>(dex_file.release()->AsCompactDexFile())); + } + for (const std::unique_ptr<const CompactDexFile>& dex_file : compact_dex_files) { + // Test that every code item is in the owned section. + const CompactDexFile::Header& header = dex_file->GetHeader(); + EXPECT_LE(header.OwnedDataBegin(), header.OwnedDataEnd()); + EXPECT_LE(header.OwnedDataBegin(), header.data_size_); + EXPECT_LE(header.OwnedDataEnd(), header.data_size_); + for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + class_def.VisitMethods(dex_file.get(), [&](const ClassDataItemIterator& it) { + if (it.GetMethodCodeItemOffset() != 0u) { + ASSERT_GE(it.GetMethodCodeItemOffset(), header.OwnedDataBegin()); + ASSERT_LT(it.GetMethodCodeItemOffset(), header.OwnedDataEnd()); + } + }); + } + // Test that the owned sections don't overlap. + for (const std::unique_ptr<const CompactDexFile>& other_dex : compact_dex_files) { + if (dex_file != other_dex) { + ASSERT_TRUE( + (dex_file->GetHeader().OwnedDataBegin() >= other_dex->GetHeader().OwnedDataEnd()) || + (dex_file->GetHeader().OwnedDataEnd() <= other_dex->GetHeader().OwnedDataBegin())); + } + } } } diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index bd76bf11d3..2b4144c611 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -298,6 +298,8 @@ void CompactDexWriter::WriteHeader(Stream* stream) { header.class_defs_off_ = collections.ClassDefsOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); + header.owned_data_begin_ = owned_data_begin_; + header.owned_data_end_ = owned_data_end_; // Compact dex specific flags. header.debug_info_offsets_pos_ = debug_info_offsets_pos_; @@ -426,6 +428,7 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { // Data section. data_stream->AlignTo(kDataSectionAlignment); } + owned_data_begin_ = data_stream->Tell(); // Write code item first to minimize the space required for encoded methods. // For cdex, the code items don't depend on the debug info. @@ -490,6 +493,7 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { WriteDebugInfoOffsetTable(data_stream); data_stream->AlignTo(kDataSectionAlignment); + owned_data_end_ = data_stream->Tell(); if (compute_offsets_) { header_->SetDataSize(data_stream->Tell()); if (header_->DataSize() != 0) { @@ -497,7 +501,6 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { main_stream->AlignTo(kDataSectionAlignment); // For now, default to saying the data is right after the main stream. header_->SetDataOffset(main_stream->Tell()); - header_->SetDataOffset(0u); } else { header_->SetDataOffset(0u); } diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index eaf85185f1..4b142a85bb 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -169,6 +169,10 @@ class CompactDexWriter : public DexWriter { // Base offset of where debug info starts in the dex file. uint32_t debug_info_base_ = 0u; + // Part of the shared data section owned by this file. + uint32_t owned_data_begin_ = 0u; + uint32_t owned_data_end_ = 0u; + // State for where we are deduping. Deduper* code_item_dedupe_ = nullptr; Deduper* data_item_dedupe_ = nullptr; diff --git a/libdexfile/dex/compact_dex_file.h b/libdexfile/dex/compact_dex_file.h index 78cd76818a..affc9a20b0 100644 --- a/libdexfile/dex/compact_dex_file.h +++ b/libdexfile/dex/compact_dex_file.h @@ -51,6 +51,16 @@ class CompactDexFile : public DexFile { return data_size_; } + // Range of the shared data section owned by the dex file. Owned in this context refers to data + // for this DEX that was not deduplicated to another DEX. + uint32_t OwnedDataBegin() const { + return owned_data_begin_; + } + + uint32_t OwnedDataEnd() const { + return owned_data_end_; + } + private: uint32_t feature_flags_ = 0u; @@ -63,6 +73,10 @@ class CompactDexFile : public DexFile { // Base offset of where debug info starts in the dex file. uint32_t debug_info_base_ = 0u; + // Range of the shared data section owned by the dex file. + uint32_t owned_data_begin_ = 0u; + uint32_t owned_data_end_ = 0u; + friend class CompactDexFile; friend class CompactDexWriter; }; diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index ae0c2f415b..d1b32007c3 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -515,6 +515,18 @@ inline const uint8_t* DexFile::GetCatchHandlerData(const DexInstructionIterator& return handler_data + offset; } +template <typename Visitor> +inline void DexFile::ClassDef::VisitMethods(const DexFile* dex_file, const Visitor& visitor) const { + const uint8_t* class_data = dex_file->GetClassData(*this); + if (class_data != nullptr) { + ClassDataItemIterator it(*dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNext(); it.Next()) { + visitor(it); + } + } +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_ diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 5560cf19c0..aeb49d2c25 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -196,6 +196,15 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(MethodId); }; + // Base code_item, compact dex and standard dex have different code item layouts. + struct CodeItem { + protected: + CodeItem() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(CodeItem); + }; + // Raw class_def_item. struct ClassDef { dex::TypeIndex class_idx_; // index into type_ids_ array for this class @@ -227,6 +236,9 @@ class DexFile { } } + template <typename Visitor> + void VisitMethods(const DexFile* dex_file, const Visitor& visitor) const; + private: DISALLOW_COPY_AND_ASSIGN(ClassDef); }; @@ -300,15 +312,6 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem); }; - // Base code_item, compact dex and standard dex have different code item layouts. - struct CodeItem { - protected: - CodeItem() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(CodeItem); - }; - // Raw try_item. struct TryItem { static constexpr size_t kAlignment = sizeof(uint32_t); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index c4bf1c7889..10da2eaa83 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1695,9 +1695,12 @@ class OatDumper { Handle<mirror::DexCache> dex_cache = hs->NewHandle( runtime->GetClassLinker()->RegisterDexFile(*dex_file, options_.class_loader_->Get())); CHECK(dex_cache != nullptr); + ArtMethod* method = runtime->GetClassLinker()->ResolveMethodWithoutInvokeType( + dex_method_idx, dex_cache, *options_.class_loader_); + CHECK(method != nullptr); return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, - class_def, code_item, nullptr, method_access_flags); + class_def, code_item, method, method_access_flags); } return nullptr; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index aa77187175..98214fb684 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -15,6 +15,7 @@ */ #include "asm_support_arm.S" +#include "interpreter/cfi_asm_support.h" #include "arch/quick_alloc_entrypoints.S" @@ -2719,3 +2720,18 @@ ENTRY art_quick_invoke_polymorphic HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) .purgem HANDLER_TABLE_OFFSET END art_quick_invoke_polymorphic + +// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. +// Argument 0: r0: The context pointer for ExecuteSwitchImpl. +// Argument 1: r1: Pointer to the templated ExecuteSwitchImpl to call. +// Argument 2: r2: The value of DEX PC (memory address of the methods bytecode). +ENTRY ExecuteSwitchImplAsm + push {r4, lr} // 2 words of callee saves. + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset r4, 0 + .cfi_rel_offset lr, 4 + mov r4, r2 // r4 = DEX PC + CFI_DEFINE_DEX_PC_WITH_OFFSET(0 /* r0 */, 4 /* r4 */, 0) + blx r1 // Call the wrapped method. + pop {r4, pc} +END ExecuteSwitchImplAsm diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 375b0506e2..fb449ed5c7 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -15,6 +15,7 @@ */ #include "asm_support_arm64.S" +#include "interpreter/cfi_asm_support.h" #include "arch/quick_alloc_entrypoints.S" @@ -2928,3 +2929,16 @@ ENTRY art_quick_invoke_polymorphic .text END art_quick_invoke_polymorphic + +// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. +// Argument 0: x0: The context pointer for ExecuteSwitchImpl. +// Argument 1: x1: Pointer to the templated ExecuteSwitchImpl to call. +// Argument 2: x2: The value of DEX PC (memory address of the methods bytecode). +ENTRY ExecuteSwitchImplAsm + SAVE_TWO_REGS_INCREASE_FRAME x19, xLR, 16 + mov x19, x2 // x19 = DEX PC + CFI_DEFINE_DEX_PC_WITH_OFFSET(0 /* x0 */, 19 /* x19 */, 0) + blr x1 // Call the wrapped method. + RESTORE_TWO_REGS_DECREASE_FRAME x19, xLR, 16 + ret +END ExecuteSwitchImplAsm diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 9251161ecc..5c4ae4ea12 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -15,6 +15,7 @@ */ #include "asm_support_x86.S" +#include "interpreter/cfi_asm_support.h" #include "arch/quick_alloc_entrypoints.S" @@ -2516,5 +2517,28 @@ END_MACRO END_FUNCTION art_quick_invoke_polymorphic +// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. +// Argument 0: ESP+4: The context pointer for ExecuteSwitchImpl. +// Argument 1: ESP+8: Pointer to the templated ExecuteSwitchImpl to call. +// Argument 2: ESP+12: The value of DEX PC (memory address of the methods bytecode). +DEFINE_FUNCTION ExecuteSwitchImplAsm + PUSH ebx // Spill EBX; Increments ESP, so arg0 is at ESP+8 now. + mov 12(%esp), %eax // EAX = C++ templated interpreter function + mov 16(%esp), %ebx // EBX = DEX PC (callee save register) + mov 8(%esp), %ecx // ECX = Context argument for the function + CFI_DEFINE_DEX_PC_WITH_OFFSET(0 /* EAX */, 3 /* EBX */, 0) + + sub LITERAL(4), %esp // Alignment padding + CFI_ADJUST_CFA_OFFSET(4) + push %ecx // Push argument + CFI_ADJUST_CFA_OFFSET(4) + call *%eax // Call the wrapped function + addl LITERAL(8), %esp + CFI_ADJUST_CFA_OFFSET(-8) + + POP ebx // Restore EBX + ret +END_FUNCTION ExecuteSwitchImplAsm + // TODO: implement these! UNIMPLEMENTED art_quick_memcmp16 diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 61168448f0..a813200606 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -15,6 +15,7 @@ */ #include "asm_support_x86_64.S" +#include "interpreter/cfi_asm_support.h" #include "arch/quick_alloc_entrypoints.S" @@ -2481,3 +2482,18 @@ END_MACRO RESTORE_SAVE_REFS_AND_ARGS_FRAME RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_invoke_polymorphic + +// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. +// Argument 0: RDI: The context pointer for ExecuteSwitchImpl. +// Argument 1: RSI: Pointer to the templated ExecuteSwitchImpl to call. +// Argument 2: RDX: The value of DEX PC (memory address of the methods bytecode). +DEFINE_FUNCTION ExecuteSwitchImplAsm + PUSH rbx // Spill RBX + movq %rdx, %rbx // RBX = DEX PC (callee save register) + CFI_DEFINE_DEX_PC_WITH_OFFSET(0 /* RAX */, 3 /* RBX */, 0) + + call *%rsi // Call the wrapped function + + POP rbx // Restore RBX + ret +END_FUNCTION ExecuteSwitchImplAsm diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 8bf91d9da1..1565644380 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -374,13 +374,14 @@ inline ObjPtr<mirror::Class> ArtMethod::ResolveReturnType() { return ResolveClassFromTypeIndex(GetReturnTypeIndex()); } +template <ReadBarrierOption kReadBarrierOption> inline bool ArtMethod::HasSingleImplementation() { - if (IsFinal() || GetDeclaringClass()->IsFinal()) { + if (IsFinal<kReadBarrierOption>() || GetDeclaringClass<kReadBarrierOption>()->IsFinal()) { // We don't set kAccSingleImplementation for these cases since intrinsic // can use the flag also. return true; } - return (GetAccessFlags() & kAccSingleImplementation) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccSingleImplementation) != 0; } inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index bbc60072b6..41b01c251b 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -43,6 +43,7 @@ #include "mirror/object_array-inl.h" #include "mirror/string.h" #include "oat_file-inl.h" +#include "quicken_info.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" @@ -88,13 +89,18 @@ ArtMethod* ArtMethod::GetNonObsoleteMethod() { } } +template <ReadBarrierOption kReadBarrierOption> ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) { - if (!IsAbstract()) { + if (!IsAbstract<kReadBarrierOption>()) { // A non-abstract's single implementation is itself. return this; } return reinterpret_cast<ArtMethod*>(GetDataPtrSize(pointer_size)); } +template ArtMethod* ArtMethod::GetSingleImplementation<ReadBarrierOption::kWithReadBarrier>( + PointerSize pointer_size); +template ArtMethod* ArtMethod::GetSingleImplementation<ReadBarrierOption::kWithoutReadBarrier>( + PointerSize pointer_size); ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) { @@ -573,6 +579,24 @@ ArrayRef<const uint8_t> ArtMethod::GetQuickenedInfo() { GetDexMethodIndex()); } +uint16_t ArtMethod::GetIndexFromQuickening(uint32_t dex_pc) { + ArrayRef<const uint8_t> data = GetQuickenedInfo(); + if (data.empty()) { + return DexFile::kDexNoIndex16; + } + QuickenInfoTable table(data); + uint32_t quicken_index = 0; + for (const DexInstructionPcPair& pair : DexInstructions()) { + if (pair.DexPc() == dex_pc) { + return table.GetData(quicken_index); + } + if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) { + ++quicken_index; + } + } + return DexFile::kDexNoIndex16; +} + const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { // Our callers should make sure they don't pass the instrumentation exit pc, // as this method does not look at the side instrumentation stack. diff --git a/runtime/art_method.h b/runtime/art_method.h index 579e554901..64d293200f 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -172,8 +172,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & synchonized) != 0; } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsFinal() { - return (GetAccessFlags() & kAccFinal) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccFinal) != 0; } template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> @@ -242,10 +243,11 @@ class ArtMethod FINAL { } // This is set by the class linker. + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsDefault() { static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0, "kAccDefault conflicts with intrinsic modifier"); - return (GetAccessFlags() & kAccDefault) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccDefault) != 0; } template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> @@ -280,8 +282,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & mask) == mask; } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsAbstract() { - return (GetAccessFlags() & kAccAbstract) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccAbstract) != 0; } bool IsSynthetic() { @@ -496,6 +499,7 @@ class ArtMethod FINAL { return DataOffset(kRuntimePointerSize); } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE bool HasSingleImplementation() REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE void SetHasSingleImplementation(bool single_impl) { @@ -513,12 +517,15 @@ class ArtMethod FINAL { ArtMethod* GetCanonicalMethod(PointerSize pointer_size = kRuntimePointerSize) REQUIRES_SHARED(Locks::mutator_lock_); + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ArtMethod* GetSingleImplementation(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size) { - DCHECK(!IsNative()); - DCHECK(IsAbstract()); // Non-abstract method's single implementation is just itself. + DCHECK(!IsNative<kReadBarrierOption>()); + // Non-abstract method's single implementation is just itself. + DCHECK(IsAbstract<kReadBarrierOption>()); SetDataPtrSize(method, pointer_size); } @@ -676,6 +683,7 @@ class ArtMethod FINAL { } ArrayRef<const uint8_t> GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); + uint16_t GetIndexFromQuickening(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method header for the compiled code containing 'pc'. Note that runtime // methods will return null for this method, as they are not oat based. diff --git a/runtime/cha.cc b/runtime/cha.cc index a53d7e5b25..f2e6a7314e 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -21,6 +21,7 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "linear_alloc.h" +#include "mirror/class_loader.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" @@ -77,6 +78,106 @@ void ClassHierarchyAnalysis::RemoveDependentsWithMethodHeaders( } } +void ClassHierarchyAnalysis::ResetSingleImplementationInHierarchy(ObjPtr<mirror::Class> klass, + const LinearAlloc* alloc, + const PointerSize pointer_size) + const { + // Presumably called from some sort of class visitor, no null pointers expected. + DCHECK(klass != nullptr); + DCHECK(alloc != nullptr); + + // Skip interfaces since they cannot provide SingleImplementations to work with. + if (klass->IsInterface()) { + return; + } + + // This method is called while visiting classes in the class table of a class loader. + // That means, some 'klass'es can belong to other classloaders. Argument 'alloc' + // allows to explicitly indicate a classloader, which is going to be deleted. + // Filter out classes, that do not belong to it. + if (!alloc->ContainsUnsafe(klass->GetMethodsPtr())) { + return; + } + + // CHA analysis is only applied to resolved classes. + if (!klass->IsResolved()) { + return; + } + + ObjPtr<mirror::Class> super = klass->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>(); + + // Skip Object class and primitive classes. + if (super == nullptr) { + return; + } + + // The class is going to be deleted. Iterate over the virtual methods of its superclasses to see + // if they have SingleImplementations methods defined by 'klass'. + // Skip all virtual methods that do not override methods from super class since they cannot be + // SingleImplementations for anything. + int32_t vtbl_size = super->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>(); + ObjPtr<mirror::ClassLoader> loader = + klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>(); + for (int vtbl_index = 0; vtbl_index < vtbl_size; ++vtbl_index) { + ArtMethod* method = + klass->GetVTableEntry<kDefaultVerifyFlags, kWithoutReadBarrier>(vtbl_index, pointer_size); + if (!alloc->ContainsUnsafe(method)) { + continue; + } + + // Find all occurrences of virtual methods in parents' SingleImplementations fields + // and reset them. + // No need to reset SingleImplementations for the method itself (it will be cleared anyways), + // so start with a superclass and move up looking into a corresponding vtbl slot. + for (ObjPtr<mirror::Class> super_it = super; + super_it != nullptr && + super_it->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>() > vtbl_index; + super_it = super_it->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>()) { + // Skip superclasses that are also going to be unloaded. + ObjPtr<mirror::ClassLoader> super_loader = super_it-> + GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>(); + if (super_loader == loader) { + continue; + } + + ArtMethod* super_method = super_it-> + GetVTableEntry<kDefaultVerifyFlags, kWithoutReadBarrier>(vtbl_index, pointer_size); + if (super_method->IsAbstract<kWithoutReadBarrier>() && + super_method->HasSingleImplementation<kWithoutReadBarrier>() && + super_method->GetSingleImplementation<kWithoutReadBarrier>(pointer_size) == method) { + // Do like there was no single implementation defined previously + // for this method of the superclass. + super_method->SetSingleImplementation<kWithoutReadBarrier>(nullptr, pointer_size); + } else { + // No related SingleImplementations could possibly be found any further. + DCHECK(!super_method->HasSingleImplementation<kWithoutReadBarrier>()); + break; + } + } + } + + // Check all possible interface methods too. + ObjPtr<mirror::IfTable> iftable = klass->GetIfTable<kDefaultVerifyFlags, kWithoutReadBarrier>(); + const size_t ifcount = klass->GetIfTableCount<kDefaultVerifyFlags, kWithoutReadBarrier>(); + for (size_t i = 0; i < ifcount; ++i) { + ObjPtr<mirror::Class> interface = + iftable->GetInterface<kDefaultVerifyFlags, kWithoutReadBarrier>(i); + for (size_t j = 0, + count = iftable->GetMethodArrayCount<kDefaultVerifyFlags, kWithoutReadBarrier>(i); + j < count; + ++j) { + ArtMethod* method = interface->GetVirtualMethod(j, pointer_size); + if (method->HasSingleImplementation<kWithoutReadBarrier>() && + alloc->ContainsUnsafe( + method->GetSingleImplementation<kWithoutReadBarrier>(pointer_size)) && + !method->IsDefault<kWithoutReadBarrier>()) { + // Do like there was no single implementation defined previously for this method. + method->SetSingleImplementation<kWithoutReadBarrier>(nullptr, pointer_size); + } + } + } +} + // This stack visitor walks the stack and for compiled code with certain method // headers, sets the should_deoptimize flag on stack to 1. // TODO: also set the register value to 1 when should_deoptimize is allocated in diff --git a/runtime/cha.h b/runtime/cha.h index 40999dd15b..d1a1b7cfae 100644 --- a/runtime/cha.h +++ b/runtime/cha.h @@ -110,6 +110,17 @@ class ClassHierarchyAnalysis { const std::unordered_set<OatQuickMethodHeader*>& method_headers) REQUIRES(Locks::cha_lock_); + // If a given class belongs to a linear allocation that is about to be deleted, in all its + // superclasses and superinterfaces reset SingleImplementation fields of their methods + // that might be affected by the deletion. + // The method is intended to be called during GC before ReclaimPhase, since it gets info from + // Java objects that are going to be collected. + // For the same reason it's important to access objects without read barrier to not revive them. + void ResetSingleImplementationInHierarchy(ObjPtr<mirror::Class> klass, + const LinearAlloc* alloc, + PointerSize pointer_size) + const REQUIRES_SHARED(Locks::mutator_lock_); + // Update CHA info for methods that `klass` overrides, after loading `klass`. void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 72c110a970..8a29ff33b7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1166,6 +1166,25 @@ static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader, return true; } +class CHAOnDeleteUpdateClassVisitor { + public: + explicit CHAOnDeleteUpdateClassVisitor(LinearAlloc* alloc) + : allocator_(alloc), cha_(Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()), + pointer_size_(Runtime::Current()->GetClassLinker()->GetImagePointerSize()), + self_(Thread::Current()) {} + + bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + // This class is going to be unloaded. Tell CHA about it. + cha_->ResetSingleImplementationInHierarchy(klass, allocator_, pointer_size_); + return true; + } + private: + const LinearAlloc* allocator_; + const ClassHierarchyAnalysis* cha_; + const PointerSize pointer_size_; + const Thread* self_; +}; + class VerifyDeclaringClassVisitor : public ArtMethodVisitor { public: VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) @@ -2150,12 +2169,14 @@ ClassLinker::~ClassLinker() { mirror::EmulatedStackFrame::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { - DeleteClassLoader(self, data); + // CHA unloading analysis is not needed. No negative consequences are expected because + // all the classloaders are deleted at the same time. + DeleteClassLoader(self, data, false /*cleanup_cha*/); } class_loaders_.clear(); } -void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) { +void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data, bool cleanup_cha) { Runtime* const runtime = Runtime::Current(); JavaVMExt* const vm = runtime->GetJavaVM(); vm->DeleteWeakGlobalRef(self, data.weak_root); @@ -2170,6 +2191,12 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) { // If we don't have a JIT, we need to manually remove the CHA dependencies manually. cha_->RemoveDependenciesForLinearAlloc(data.allocator); } + // Cleanup references to single implementation ArtMethods that will be deleted. + if (cleanup_cha) { + CHAOnDeleteUpdateClassVisitor visitor(data.allocator); + data.class_table->Visit<CHAOnDeleteUpdateClassVisitor, kWithoutReadBarrier>(visitor); + } + delete data.allocator; delete data.class_table; } @@ -5836,6 +5863,14 @@ bool ClassLinker::LinkVirtualMethods( // smaller as we go on. uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator); if (hash_index != hash_table.GetNotFoundIndex()) { + // Run a check whether we are going to override a method which is hidden + // to `klass`, but ignore the result as we only warn at the moment. + // We cannot do this test earlier because we need to establish that + // a method is being overridden first. ShouldBlockAccessToMember would + // print bogus warnings otherwise. + hiddenapi::ShouldBlockAccessToMember( + super_method, klass->GetClassLoader(), hiddenapi::kOverride); + ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking( hash_index, image_pointer_size_); if (super_method->IsFinal()) { @@ -7907,6 +7942,10 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, resolved = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_); } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); + if (resolved != nullptr && + hiddenapi::ShouldBlockAccessToMember(resolved, class_loader, hiddenapi::kLinking)) { + resolved = nullptr; + } if (resolved != nullptr) { // In case of jmvti, the dex file gets verified before being registered, so first // check if it's registered before checking class tables. @@ -8045,7 +8084,10 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, } else { resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } - + if (resolved != nullptr && + hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { + resolved = nullptr; + } return resolved; } @@ -8119,11 +8161,16 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } else { resolved = klass->FindInstanceField(name, type); } - if (resolved == nullptr) { - ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); - return nullptr; - } } + + if (resolved == nullptr || + hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); + return nullptr; + } + dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_); return resolved; } @@ -8149,6 +8196,10 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, StringPiece name(dex_file.GetFieldName(field_id)); StringPiece type(dex_file.GetFieldTypeDescriptor(field_id)); resolved = mirror::Class::FindField(self, klass, name, type); + if (resolved != nullptr && + hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { + resolved = nullptr; + } if (resolved != nullptr) { dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_); } else { @@ -8236,29 +8287,34 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( DexFile::MethodHandleType handle_type = static_cast<DexFile::MethodHandleType>(method_handle.method_handle_type_); mirror::MethodHandle::Kind kind; + bool is_put; bool is_static; int32_t num_params; switch (handle_type) { case DexFile::MethodHandleType::kStaticPut: { kind = mirror::MethodHandle::Kind::kStaticPut; + is_put = true; is_static = true; num_params = 1; break; } case DexFile::MethodHandleType::kStaticGet: { kind = mirror::MethodHandle::Kind::kStaticGet; + is_put = false; is_static = true; num_params = 0; break; } case DexFile::MethodHandleType::kInstancePut: { kind = mirror::MethodHandle::Kind::kInstancePut; + is_put = true; is_static = false; num_params = 2; break; } case DexFile::MethodHandleType::kInstanceGet: { kind = mirror::MethodHandle::Kind::kInstanceGet; + is_put = false; is_static = false; num_params = 1; break; @@ -8280,6 +8336,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( ThrowIllegalAccessErrorField(referring_class, target_field); return nullptr; } + if (UNLIKELY(is_put && target_field->IsFinal())) { + ThrowIllegalAccessErrorField(referring_class, target_field); + return nullptr; + } } else { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8895,7 +8955,8 @@ void ClassLinker::CleanupClassLoaders() { } } for (ClassLoaderData& data : to_delete) { - DeleteClassLoader(self, data); + // CHA unloading analysis and SingleImplementaion cleanups are required. + DeleteClassLoader(self, data, true /*cleanup_cha*/); } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 8d6b3d245c..d05e78fb40 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -752,7 +752,7 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void DeleteClassLoader(Thread* self, const ClassLoaderData& data) + void DeleteClassLoader(Thread* self, const ClassLoaderData& data, bool cleanup_cha) REQUIRES_SHARED(Locks::mutator_lock_); void VisitClassesInternal(ClassVisitor* visitor) diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index 718e93a97d..c59e2e881d 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -60,12 +60,12 @@ void ClassTable::VisitRoots(const Visitor& visitor) { } } -template <typename Visitor> +template <typename Visitor, ReadBarrierOption kReadBarrierOption> bool ClassTable::Visit(Visitor& visitor) { ReaderMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { for (TableSlot& table_slot : class_set) { - if (!visitor(table_slot.Read())) { + if (!visitor(table_slot.Read<kReadBarrierOption>())) { return false; } } @@ -73,12 +73,12 @@ bool ClassTable::Visit(Visitor& visitor) { return true; } -template <typename Visitor> +template <typename Visitor, ReadBarrierOption kReadBarrierOption> bool ClassTable::Visit(const Visitor& visitor) { ReaderMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { for (TableSlot& table_slot : class_set) { - if (!visitor(table_slot.Read())) { + if (!visitor(table_slot.Read<kReadBarrierOption>())) { return false; } } diff --git a/runtime/class_table.h b/runtime/class_table.h index 52e9f82396..3e90fe2768 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -190,11 +190,11 @@ class ClassTable { REQUIRES_SHARED(Locks::mutator_lock_); // Stops visit if the visitor returns false. - template <typename Visitor> + template <typename Visitor, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool Visit(Visitor& visitor) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - template <typename Visitor> + template <typename Visitor, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool Visit(const Visitor& visitor) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 7484dd9207..945442d539 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -34,6 +34,7 @@ #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" #include "thread.h" +#include "vdex_file.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" @@ -608,13 +609,10 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { break; case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { - // Since we replaced the method index, we ask the verifier to tell us which - // method is invoked at this location. - ArtMethod* invoked_method = - verifier::MethodVerifier::FindInvokedMethodAtDexPc(method, throw_dex_pc); - if (invoked_method != nullptr) { + uint16_t method_idx = method->GetIndexFromQuickening(throw_dex_pc); + if (method_idx != DexFile::kDexNoIndex16) { // NPE with precise message. - ThrowNullPointerExceptionForMethodAccess(invoked_method, kVirtual); + ThrowNullPointerExceptionForMethodAccess(method_idx, kVirtual); } else { // NPE with imprecise message. ThrowNullPointerException("Attempt to invoke a virtual method on a null object reference"); @@ -641,17 +639,13 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IGET_SHORT_QUICK: case Instruction::IGET_WIDE_QUICK: case Instruction::IGET_OBJECT_QUICK: { - // Since we replaced the field index, we ask the verifier to tell us which - // field is accessed at this location. - ArtField* field = - verifier::MethodVerifier::FindAccessedFieldAtDexPc(method, throw_dex_pc); - if (field != nullptr) { - // NPE with precise message. - ThrowNullPointerExceptionForFieldAccess(field, true /* read */); - } else { - // NPE with imprecise message. - ThrowNullPointerException("Attempt to read from a field on a null object reference"); - } + uint16_t field_idx = method->GetIndexFromQuickening(throw_dex_pc); + ArtField* field = nullptr; + CHECK_NE(field_idx, DexFile::kDexNoIndex16); + field = Runtime::Current()->GetClassLinker()->ResolveField( + field_idx, method, /* is_static */ false); + Thread::Current()->ClearException(); // Resolution may fail, ignore. + ThrowNullPointerExceptionForFieldAccess(field, true /* read */); break; } case Instruction::IPUT: @@ -661,8 +655,8 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { - ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); + ArtField* field = Runtime::Current()->GetClassLinker()->ResolveField( + instr.VRegC_22c(), method, /* is_static */ false); Thread::Current()->ClearException(); // Resolution may fail, ignore. ThrowNullPointerExceptionForFieldAccess(field, false /* write */); break; @@ -674,17 +668,13 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IPUT_SHORT_QUICK: case Instruction::IPUT_WIDE_QUICK: case Instruction::IPUT_OBJECT_QUICK: { - // Since we replaced the field index, we ask the verifier to tell us which - // field is accessed at this location. - ArtField* field = - verifier::MethodVerifier::FindAccessedFieldAtDexPc(method, throw_dex_pc); - if (field != nullptr) { - // NPE with precise message. - ThrowNullPointerExceptionForFieldAccess(field, false /* write */); - } else { - // NPE with imprecise message. - ThrowNullPointerException("Attempt to write to a field on a null object reference"); - } + uint16_t field_idx = method->GetIndexFromQuickening(throw_dex_pc); + ArtField* field = nullptr; + CHECK_NE(field_idx, DexFile::kDexNoIndex16); + field = Runtime::Current()->GetClassLinker()->ResolveField( + field_idx, method, /* is_static */ false); + Thread::Current()->ClearException(); // Resolution may fail, ignore. + ThrowNullPointerExceptionForFieldAccess(field, false /* write */); break; } case Instruction::AGET: diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 404c5357bf..270bce2129 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -81,13 +81,14 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, // Lookup the declaring class of the inlined method. ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache(); - const DexFile* dex_file = dex_cache->GetDexFile(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index); ArtMethod* inlined_method = dex_cache->GetResolvedMethod(method_index, kRuntimePointerSize); if (inlined_method != nullptr) { DCHECK(!inlined_method->IsRuntimeMethod()); return inlined_method; } + // TODO: Use ClassLoader::LookupResolvedMethod() instead. + const DexFile* dex_file = dex_cache->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index); const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index e9a0808843..b33587204b 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1175,6 +1175,97 @@ extern "C" TwoWordReturn artInstrumentationMethodExitFromCode(Thread* self, return return_or_deoptimize_pc; } +static std::string DumpInstruction(ArtMethod* method, uint32_t dex_pc) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (dex_pc == static_cast<uint32_t>(-1)) { + CHECK(method == jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt)); + return "<native>"; + } else { + CodeItemInstructionAccessor accessor = method->DexInstructions(); + CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits()); + return accessor.InstructionAt(dex_pc).DumpString(method->GetDexFile()); + } +} + +static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { + // Mimick the search for the caller and dump some data while doing so. + LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data for b/74410240."; + + constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs; + CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); + + const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type); + auto** caller_sp = reinterpret_cast<ArtMethod**>( + reinterpret_cast<uintptr_t>(sp) + callee_frame_size); + const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type); + uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>( + (reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset)); + ArtMethod* outer_method = *caller_sp; + + if (UNLIKELY(caller_pc == reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()))) { + LOG(FATAL_WITHOUT_ABORT) << "Method: " << outer_method->PrettyMethod() + << " native pc: " << caller_pc << " Instrumented!"; + return; + } + + const OatQuickMethodHeader* current_code = outer_method->GetOatQuickMethodHeader(caller_pc); + CHECK(current_code != nullptr); + CHECK(current_code->IsOptimized()); + uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc); + CodeInfo code_info = current_code->GetOptimizedCodeInfo(); + MethodInfo method_info = current_code->GetOptimizedMethodInfo(); + CodeInfoEncoding encoding = code_info.ExtractEncoding(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + CHECK(stack_map.IsValid()); + uint32_t dex_pc = stack_map.GetDexPc(encoding.stack_map.encoding); + + // Log the outer method and its associated dex file and class table pointer which can be used + // to find out if the inlined methods were defined by other dex file(s) or class loader(s). + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + LOG(FATAL_WITHOUT_ABORT) << "Outer: " << outer_method->PrettyMethod() + << " native pc: " << caller_pc + << " dex pc: " << dex_pc + << " dex file: " << outer_method->GetDexFile()->GetLocation() + << " class table: " << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader()); + LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(outer_method, dex_pc); + + ArtMethod* caller = outer_method; + if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + const InlineInfoEncoding& inline_info_encoding = encoding.inline_info.encoding; + size_t depth = inline_info.GetDepth(inline_info_encoding); + for (size_t d = 0; d < depth; ++d) { + const char* tag = ""; + dex_pc = inline_info.GetDexPcAtDepth(inline_info_encoding, d); + if (inline_info.EncodesArtMethodAtDepth(inline_info_encoding, d)) { + tag = "encoded "; + caller = inline_info.GetArtMethodAtDepth(inline_info_encoding, d); + } else { + uint32_t method_index = inline_info.GetMethodIndexAtDepth(inline_info_encoding, + method_info, + d); + if (dex_pc == static_cast<uint32_t>(-1)) { + tag = "special "; + CHECK_EQ(d + 1u, depth); + caller = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); + CHECK_EQ(caller->GetDexMethodIndex(), method_index); + } else { + ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache(); + ObjPtr<mirror::ClassLoader> class_loader = caller->GetClassLoader(); + caller = class_linker->LookupResolvedMethod(method_index, dex_cache, class_loader); + CHECK(caller != nullptr); + } + } + LOG(FATAL_WITHOUT_ABORT) << "Inlined method #" << d << ": " << tag << caller->PrettyMethod() + << " dex pc: " << dex_pc + << " dex file: " << caller->GetDexFile()->GetLocation() + << " class table: " + << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader()); + LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(caller, dex_pc); + } + } +} + // Lazily resolve a method for quick. Called by stub code. extern "C" const void* artQuickResolutionTrampoline( ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp) @@ -1255,6 +1346,7 @@ extern "C" const void* artQuickResolutionTrampoline( is_range = true; break; default: + DumpB74410240DebugData(sp); LOG(FATAL) << "Unexpected call into trampoline: " << instr.DumpString(nullptr); UNREACHABLE(); } diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index e0519a07da..f2ea2fdaaa 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -17,7 +17,10 @@ #ifndef ART_RUNTIME_HIDDEN_API_H_ #define ART_RUNTIME_HIDDEN_API_H_ +#include "art_field-inl.h" +#include "art_method-inl.h" #include "dex/hidden_api_access_flags.h" +#include "mirror/class-inl.h" #include "reflection.h" #include "runtime.h" @@ -33,7 +36,9 @@ enum Action { enum AccessMethod { kReflection, - kJNI + kJNI, + kLinking, + kOverride, }; inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { @@ -44,6 +49,12 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { case kJNI: os << "JNI"; break; + case kLinking: + os << "linking"; + break; + case kOverride: + os << "override"; + break; } return os; } @@ -88,7 +99,7 @@ inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method) // class path or not. Because different users of this function determine this // in a different way, `fn_caller_in_boot(self)` is called and should return // true if the caller is in boot class path. -// This function might print warnings into the log if the member is greylisted. +// This function might print warnings into the log if the member is hidden. template<typename T> inline bool ShouldBlockAccessToMember(T* member, Thread* self, @@ -145,27 +156,19 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } -// Returns true if access to member with `access_flags` should be denied to `caller`. -// This function should be called on statically linked uses of hidden API. -inline bool ShouldBlockAccessToMember(uint32_t access_flags, mirror::Class* caller) +// Returns true if access to `member` should be denied to a caller loaded with +// `caller_class_loader`. +// This function might print warnings into the log if the member is hidden. +template<typename T> +inline bool ShouldBlockAccessToMember(T* member, + ObjPtr<mirror::ClassLoader> caller_class_loader, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - if (!Runtime::Current()->AreHiddenApiChecksEnabled()) { - // Exit early. Nothing to enforce. - return false; - } - - // Only continue if we want to deny access. Warnings are *not* printed. - if (GetMemberAction(access_flags) != kDeny) { - return false; - } - - // Member is hidden. Check if the caller is in boot class path. - if (caller == nullptr) { - // The caller is unknown. We assume that this is *not* boot class path. - return true; - } - - return !caller->IsBootStrapClassLoaded(); + bool caller_in_boot = (caller_class_loader.IsNull()); + return ShouldBlockAccessToMember(member, + /* thread */ nullptr, + [caller_in_boot] (Thread*) { return caller_in_boot; }, + access_method); } } // namespace hiddenapi diff --git a/runtime/interpreter/cfi_asm_support.h b/runtime/interpreter/cfi_asm_support.h new file mode 100644 index 0000000000..c1e5fb5615 --- /dev/null +++ b/runtime/interpreter/cfi_asm_support.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 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_RUNTIME_INTERPRETER_CFI_ASM_SUPPORT_H_ +#define ART_RUNTIME_INTERPRETER_CFI_ASM_SUPPORT_H_ + +#if !defined(__APPLE__) + /* + * Define the DEX PC (memory address of the currently interpreted bytecode) + * within the CFI stream of the current function (stored in .eh_frame). + * This allows libunwind to detect that the frame is in the interpreter, + * and to resolve the memory address into human readable Java method name. + * The CFI instruction is recognised by the magic bytes in the expression + * (we push magic "DEX1" constant on the DWARF stack and drop it again). + * + * As with any other CFI opcode, the expression needs to be associated with + * a register. Any caller-save register will do as those are unused in CFI. + * Better solution would be to store the expression in Android-specific + * DWARF register (CFI registers don't have to correspond to real hardware + * registers), however, gdb handles any unknown registers very poorly. + * Similarly, we could also use some of the user-defined opcodes defined + * in the DWARF specification, but gdb doesn't support those either. + * + * The DEX PC is generally advanced in the middle of the bytecode handler, + * which will result in the reported DEX PC to be off by an instruction. + * Therefore the macro allows adding/subtracting an offset to compensate. + * TODO: Add the offsets to handlers to get line-accurate DEX PC reporting. + */ + #define CFI_DEFINE_DEX_PC_WITH_OFFSET(tmpReg, dexReg, dexOffset) .cfi_escape \ + 0x16 /* DW_CFA_val_expression */, tmpReg, 0x09 /* size */, \ + 0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = "DEX1" */ \ + 0x13 /* DW_OP_drop */, \ + 0x92 /* DW_OP_bregx */, dexReg, (dexOffset & 0x7F) /* 1-byte SLEB128 */ +#else + // Mac OS doesn't like cfi_* directives. + #define CFI_DEFINE_DEX_PC_WITH_OFFSET(tmpReg, dexReg, dexOffset) +#endif + +#endif // ART_RUNTIME_INTERPRETER_CFI_ASM_SUPPORT_H_ diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index e35d80f724..283885e522 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -39,7 +39,8 @@ namespace interpreter { /* Signal mterp to return to caller */ \ shadow_frame.SetDexPC(dex::kDexNoIndex); \ } \ - return JValue(); /* Handled in caller. */ \ + ctx->result = JValue(); /* Handled in caller. */ \ + return; \ } else { \ int32_t displacement = \ static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc); \ @@ -96,7 +97,8 @@ namespace interpreter { /* OSR has completed execution of the method. Signal mterp to return to caller */ \ shadow_frame.SetDexPC(dex::kDexNoIndex); \ } \ - return result; \ + ctx->result = result; \ + return; \ } \ } while (false) @@ -193,13 +195,17 @@ NO_INLINE static bool SendMethodExitEvents(Thread* self, } template<bool do_access_check, bool transaction_active> -JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction) { +void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { + Thread* self = ctx->self; + const CodeItemDataAccessor& accessor = ctx->accessor; + ShadowFrame& shadow_frame = ctx->shadow_frame; + JValue result_register = ctx->result_register; + bool interpret_one_instruction = ctx->interpret_one_instruction; constexpr bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; - return JValue(); + ctx->result = JValue(); + return; } self->VerifyStack(); @@ -317,7 +323,8 @@ JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, /* Signal mterp to return to caller */ shadow_frame.SetDexPC(dex::kDexNoIndex); } - return result; + ctx->result = result; + return; } case Instruction::RETURN_VOID: { PREAMBLE(); @@ -339,7 +346,8 @@ JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, /* Signal mterp to return to caller */ shadow_frame.SetDexPC(dex::kDexNoIndex); } - return result; + ctx->result = result; + return; } case Instruction::RETURN: { PREAMBLE(); @@ -362,7 +370,8 @@ JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, /* Signal mterp to return to caller */ shadow_frame.SetDexPC(dex::kDexNoIndex); } - return result; + ctx->result = result; + return; } case Instruction::RETURN_WIDE: { PREAMBLE(); @@ -384,7 +393,8 @@ JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, /* Signal mterp to return to caller */ shadow_frame.SetDexPC(dex::kDexNoIndex); } - return result; + ctx->result = result; + return; } case Instruction::RETURN_OBJECT: { PREAMBLE(); @@ -428,7 +438,8 @@ JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, /* Signal mterp to return to caller */ shadow_frame.SetDexPC(dex::kDexNoIndex); } - return result; + ctx->result = result; + return; } case Instruction::CONST_4: { PREAMBLE(); @@ -2487,26 +2498,19 @@ JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, } while (!interpret_one_instruction); // Record where we stopped. shadow_frame.SetDexPC(inst->GetDexPc(insns)); - return result_register; + ctx->result = result_register; + return; } // NOLINT(readability/fn_size) -// Explicit definitions of ExecuteSwitchImpl. +// Explicit definitions of ExecuteSwitchImplCpp. template HOT_ATTR -JValue ExecuteSwitchImpl<true, false>(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp<true, false>(SwitchImplContext* ctx); template HOT_ATTR -JValue ExecuteSwitchImpl<false, false>(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp<false, false>(SwitchImplContext* ctx); template -JValue ExecuteSwitchImpl<true, true>(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp<true, true>(SwitchImplContext* ctx); template -JValue ExecuteSwitchImpl<false, true>(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp<false, true>(SwitchImplContext* ctx); } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/interpreter_switch_impl.h b/runtime/interpreter/interpreter_switch_impl.h index 50db337f03..9fc4239d05 100644 --- a/runtime/interpreter/interpreter_switch_impl.h +++ b/runtime/interpreter/interpreter_switch_impl.h @@ -20,23 +20,59 @@ #include "base/macros.h" #include "base/mutex.h" #include "dex/dex_file.h" +#include "dex/code_item_accessors.h" #include "jvalue.h" #include "obj_ptr.h" namespace art { -class CodeItemDataAccessor; class ShadowFrame; class Thread; namespace interpreter { +// Group all the data that is needed in the switch interpreter. +// We need to pass it to the hand-written assembly and back, +// so it is easier to pass it through a single pointer. +// Similarly, returning the JValue type would be non-trivial. +struct SwitchImplContext { + Thread* self; + const CodeItemDataAccessor& accessor; + ShadowFrame& shadow_frame; + JValue& result_register; + bool interpret_one_instruction; + JValue result; +}; + +// The actual internal implementation of the switch interpreter. +template<bool do_access_check, bool transaction_active> +void ExecuteSwitchImplCpp(SwitchImplContext* ctx) + REQUIRES_SHARED(Locks::mutator_lock_); + +// Hand-written assembly method which wraps the C++ implementation, +// while defining the DEX PC in the CFI so that libunwind can resolve it. +extern "C" void ExecuteSwitchImplAsm(SwitchImplContext* ctx, void* impl, const uint16_t* dexpc) + REQUIRES_SHARED(Locks::mutator_lock_); + +// Wrapper around the switch interpreter which ensures we can unwind through it. template<bool do_access_check, bool transaction_active> -JValue ExecuteSwitchImpl(Thread* self, - const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, - JValue result_register, - bool interpret_one_instruction) REQUIRES_SHARED(Locks::mutator_lock_); +ALWAYS_INLINE JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, + ShadowFrame& shadow_frame, JValue result_register, + bool interpret_one_instruction) + REQUIRES_SHARED(Locks::mutator_lock_) { + SwitchImplContext ctx { + .self = self, + .accessor = accessor, + .shadow_frame = shadow_frame, + .result_register = result_register, + .interpret_one_instruction = interpret_one_instruction, + .result = JValue(), + }; + void* impl = reinterpret_cast<void*>(&ExecuteSwitchImplCpp<do_access_check, transaction_active>); + const uint16_t* dex_pc = ctx.accessor.Insns(); + ExecuteSwitchImplAsm(&ctx, impl, dex_pc); + return ctx.result; +} } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S index 1f15f870ea..8d9cab5a2f 100644 --- a/runtime/interpreter/mterp/arm/header.S +++ b/runtime/interpreter/mterp/arm/header.S @@ -85,7 +85,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index f0bf8ca34e..7017dd149c 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -87,7 +87,7 @@ codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/cfi_asm_support.h b/runtime/interpreter/mterp/cfi_asm_support.h deleted file mode 100644 index 0df4eb4f81..0000000000 --- a/runtime/interpreter/mterp/cfi_asm_support.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2017 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_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ -#define ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ - -/* - * Define the DEX PC (memory address of the currently interpreted bytecode) - * within the CFI stream of the current function (stored in .eh_frame). - * This allows libunwind to detect that the frame is in the interpreter, - * and to resolve the memory address into human readable Java method name. - * The CFI instruction is recognised by the magic bytes in the expression - * (we push magic "DEX1" constant on the DWARF stack and drop it again). - * - * As with any other CFI opcode, the expression needs to be associated with - * a register. Any caller-save register will do as those are unused in CFI. - * Better solution would be to store the expression in Android-specific - * DWARF register (CFI registers don't have to correspond to real hardware - * registers), however, gdb handles any unknown registers very poorly. - * Similarly, we could also use some of the user-defined opcodes defined - * in the DWARF specification, but gdb doesn't support those either. - * - * The DEX PC is generally advanced in the middle of the bytecode handler, - * which will result in the reported DEX PC to be off by an instruction. - * Therefore the macro allows adding/subtracting an offset to compensate. - * TODO: Add the offsets to handlers to get line-accurate DEX PC reporting. - */ -#define CFI_DEFINE_DEX_PC_WITH_OFFSET(tmpReg, dexReg, dexOffset) .cfi_escape \ - 0x16 /* DW_CFA_val_expression */, tmpReg, 0x09 /* size */, \ - 0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = "DEX1" */ \ - 0x13 /* DW_OP_drop */, \ - 0x92 /* DW_OP_bregx */, dexReg, (dexOffset & 0x7F) /* 1-byte SLEB128 */ - -#endif // ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ diff --git a/runtime/interpreter/mterp/gen_mterp.py b/runtime/interpreter/mterp/gen_mterp.py index 40d99df037..64114d747a 100755 --- a/runtime/interpreter/mterp/gen_mterp.py +++ b/runtime/interpreter/mterp/gen_mterp.py @@ -22,7 +22,7 @@ import sys, string, re, time from string import Template -interp_defs_file = "../../dex/dex_instruction_list.h" # need opcode list +interp_defs_file = "../../../libdexfile/dex/dex_instruction_list.h" # need opcode list kNumPackedOpcodes = 256 splitops = False diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S index 014628f415..bef9eeb7f2 100644 --- a/runtime/interpreter/mterp/mips/header.S +++ b/runtime/interpreter/mterp/mips/header.S @@ -32,7 +32,7 @@ */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S index 4947aff38e..7e1446c0c6 100644 --- a/runtime/interpreter/mterp/mips64/header.S +++ b/runtime/interpreter/mterp/mips64/header.S @@ -104,7 +104,7 @@ The following registers have fixed assignments: * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" /* * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 5c1a13b9d6..7ea79821b4 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -92,7 +92,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index 72446ba082..d5374d2a8a 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -94,7 +94,7 @@ codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index d5861b28a7..69568eaf44 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -39,7 +39,7 @@ */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 5224df92be..83a6613e37 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -111,7 +111,7 @@ The following registers have fixed assignments: * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" /* * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index f98fa5b74f..6f4752f312 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -95,7 +95,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" /* * Handle mac compiler specific diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index d82a2d2eb0..fca2515698 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -91,7 +91,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" /* * Handle mac compiler specific diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 2e3bbdf6f7..9d826c2ce2 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -88,7 +88,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" /* * Handle mac compiler specific diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S index eabaade4e7..55638106ed 100644 --- a/runtime/interpreter/mterp/x86_64/header.S +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -84,7 +84,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.h" +#include "interpreter/cfi_asm_support.h" /* * Handle mac compiler specific diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 600561b85c..76df65f730 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -182,7 +182,9 @@ template<typename T> static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame) REQUIRES_SHARED(Locks::mutator_lock_) { return hiddenapi::ShouldBlockAccessToMember( - member->GetAccessFlags(), frame->GetMethod()->GetDeclaringClass()); + member, + frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), + hiddenapi::kReflection); // all uses in this file are from reflection } void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc index 505e626f67..63fb22cfce 100644 --- a/runtime/jit/debugger_interface.cc +++ b/runtime/jit/debugger_interface.cc @@ -24,7 +24,9 @@ #include "thread-current-inl.h" #include "thread.h" +#include <atomic> #include <unordered_map> +#include <cstddef> // // Debug interface for native tools (gdb, lldb, libunwind, simpleperf). @@ -37,13 +39,40 @@ // method, which is called after every modification of the linked list. // GDB does this, but it is complex to set up and it stops the process. // -// 2) Asynchronously, by monitoring the action_counter_, which is incremented -// on every modification of the linked list and kept at -1 during updates. -// Therefore, if the tool process reads the counter both before and after -// iterating over the linked list, and the counters match and are not -1, -// the tool process can be sure the list was not modified during the read. -// Obviously, it can also cache the data and use the counter to determine -// if the cache is up to date, or to intelligently update it if needed. +// 2) Asynchronously, by monitoring the action_seqlock_. +// * The seqlock is a monotonically increasing counter which is incremented +// before and after every modification of the linked list. Odd value of +// the counter means the linked list is being modified (it is locked). +// * The tool should read the value of the seqlock both before and after +// copying the linked list. If the seqlock values match and are even, +// the copy is consistent. Otherwise, the reader should try again. +// * Note that using the data directly while is it being modified +// might crash the tool. Therefore, the only safe way is to make +// a copy and use the copy only after the seqlock has been checked. +// * Note that the process might even free and munmap the data while +// it is being copied, therefore the reader should either handle +// SEGV or use OS calls to read the memory (e.g. process_vm_readv). +// * The seqlock can be used to determine the number of modifications of +// the linked list, which can be used to intelligently cache the data. +// Note the possible overflow of the seqlock. It is intentionally +// 32-bit, since 64-bit atomics can be tricky on some architectures. +// * The timestamps on the entry record the time when the entry was +// created which is relevant if the unwinding is not live and is +// postponed until much later. All timestamps must be unique. +// * Memory barriers are used to make it possible to reason about +// the data even when it is being modified (e.g. the process crashed +// while that data was locked, and thus it will be never unlocked). +// * In particular, it should be possible to: +// 1) read the seqlock and then the linked list head pointer. +// 2) copy the entry and check that seqlock has not changed. +// 3) copy the symfile and check that seqlock has not changed. +// 4) go back to step 2 using the next pointer (if non-null). +// This safely creates copy of all symfiles, although other data +// might be inconsistent/unusable (e.g. prev_, action_timestamp_). +// * For full conformance with the C++ memory model, all seqlock +// protected accesses should be atomic. We currently do this in the +// more critical cases. The rest will have to be fixed before +// attempting to run TSAN on this code. // namespace art { @@ -55,7 +84,10 @@ extern "C" { } JITAction; struct JITCodeEntry { - JITCodeEntry* next_; + // Atomic to ensure the reader can always iterate over the linked list + // (e.g. the process could crash in the middle of writing this field). + std::atomic<JITCodeEntry*> next_; + // Non-atomic. The reader should not use it. It is only used for deletion. JITCodeEntry* prev_; const uint8_t* symfile_addr_; uint64_t symfile_size_; // Beware of the offset (12 on x86; but 16 on ARM32). @@ -65,24 +97,25 @@ extern "C" { }; struct JITDescriptor { - uint32_t version_ = 1; // NB: GDB supports only version 1. - uint32_t action_flag_ = JIT_NOACTION; // One of the JITAction enum values. - JITCodeEntry* relevant_entry_ = nullptr; // The entry affected by the action. - JITCodeEntry* first_entry_ = nullptr; // Head of link list of all entries. + uint32_t version_ = 1; // NB: GDB supports only version 1. + uint32_t action_flag_ = JIT_NOACTION; // One of the JITAction enum values. + JITCodeEntry* relevant_entry_ = nullptr; // The entry affected by the action. + std::atomic<JITCodeEntry*> head_{nullptr}; // Head of link list of all entries. // Android-specific fields: uint8_t magic_[8] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', '1'}; - uint32_t flags_ = 0; // Reserved for future use. Must be 0. + uint32_t flags_ = 0; // Reserved for future use. Must be 0. uint32_t sizeof_descriptor = sizeof(JITDescriptor); uint32_t sizeof_entry = sizeof(JITCodeEntry); - std::atomic_int32_t action_counter_; // Number of actions, or -1 if locked. - // It can overflow from INT32_MAX to 0. - uint64_t action_timestamp_ = 1; // CLOCK_MONOTONIC time of last action. + std::atomic_uint32_t action_seqlock_{0}; // Incremented before and after any modification. + uint64_t action_timestamp_ = 1; // CLOCK_MONOTONIC time of last action. }; - // Check that std::atomic_int32_t has the same layout as int32_t. - static_assert(alignof(std::atomic_int32_t) == alignof(int32_t), "Weird alignment"); - static_assert(sizeof(std::atomic_int32_t) == sizeof(int32_t), "Weird size"); + // Check that std::atomic has the expected layout. + static_assert(alignof(std::atomic_uint32_t) == alignof(uint32_t), "Weird alignment"); + static_assert(sizeof(std::atomic_uint32_t) == sizeof(uint32_t), "Weird size"); + static_assert(alignof(std::atomic<void*>) == alignof(void*), "Weird alignment"); + static_assert(sizeof(std::atomic<void*>) == sizeof(void*), "Weird size"); // GDB may set breakpoint here. We must ensure it is not removed or deduplicated. void __attribute__((noinline)) __jit_debug_register_code() { @@ -103,17 +136,20 @@ extern "C" { JITDescriptor __dex_debug_descriptor {}; } -// Mark the descriptor as "locked", so native tools know the data is unstable. -// Returns the old value of the counter. -static int32_t LockActionCounter(JITDescriptor& descriptor) { - return descriptor.action_counter_.exchange(-1); +// Mark the descriptor as "locked", so native tools know the data is being modified. +static void ActionSeqlock(JITDescriptor& descriptor) { + DCHECK_EQ(descriptor.action_seqlock_.load() & 1, 0u) << "Already locked"; + descriptor.action_seqlock_.fetch_add(1, std::memory_order_relaxed); + // Ensure that any writes within the locked section cannot be reordered before the increment. + std::atomic_thread_fence(std::memory_order_release); } // Mark the descriptor as "unlocked", so native tools know the data is safe to read. -// It will also increment the value so that the tools know the data has changed. -static void UnlockActionCounter(JITDescriptor& descriptor, int32_t old_value) { - int32_t new_value = (old_value + 1) & 0x7FFFFFFF; // Handle overflow to avoid -1. - descriptor.action_counter_.store(new_value); +static void ActionSequnlock(JITDescriptor& descriptor) { + DCHECK_EQ(descriptor.action_seqlock_.load() & 1, 1u) << "Already unlocked"; + // Ensure that any writes within the locked section cannot be reordered after the increment. + std::atomic_thread_fence(std::memory_order_release); + descriptor.action_seqlock_.fetch_add(1, std::memory_order_relaxed); } static JITCodeEntry* CreateJITCodeEntryInternal( @@ -121,23 +157,29 @@ static JITCodeEntry* CreateJITCodeEntryInternal( void (*register_code_ptr)(), const ArrayRef<const uint8_t>& symfile) REQUIRES(Locks::native_debug_interface_lock_) { - int32_t old_action_counter = LockActionCounter(descriptor); + // Ensure the timestamp is monotonically increasing even in presence of low + // granularity system timer. This ensures each entry has unique timestamp. + uint64_t timestamp = std::max(descriptor.action_timestamp_ + 1, NanoTime()); + JITCodeEntry* head = descriptor.head_.load(std::memory_order_relaxed); JITCodeEntry* entry = new JITCodeEntry; CHECK(entry != nullptr); entry->symfile_addr_ = symfile.data(); entry->symfile_size_ = symfile.size(); entry->prev_ = nullptr; - entry->next_ = descriptor.first_entry_; - entry->register_timestamp_ = NanoTime(); - if (entry->next_ != nullptr) { - entry->next_->prev_ = entry; + entry->next_.store(head, std::memory_order_relaxed); + entry->register_timestamp_ = timestamp; + + // We are going to modify the linked list, so take the seqlock. + ActionSeqlock(descriptor); + if (head != nullptr) { + head->prev_ = entry; } - descriptor.first_entry_ = entry; + descriptor.head_.store(entry, std::memory_order_relaxed); descriptor.relevant_entry_ = entry; descriptor.action_flag_ = JIT_REGISTER_FN; - descriptor.action_timestamp_ = entry->register_timestamp_; - UnlockActionCounter(descriptor, old_action_counter); + descriptor.action_timestamp_ = timestamp; + ActionSequnlock(descriptor); (*register_code_ptr)(); return entry; @@ -149,23 +191,35 @@ static void DeleteJITCodeEntryInternal( JITCodeEntry* entry) REQUIRES(Locks::native_debug_interface_lock_) { CHECK(entry != nullptr); - int32_t old_action_counter = LockActionCounter(descriptor); + // Ensure the timestamp is monotonically increasing even in presence of low + // granularity system timer. This ensures each entry has unique timestamp. + uint64_t timestamp = std::max(descriptor.action_timestamp_ + 1, NanoTime()); + + // We are going to modify the linked list, so take the seqlock. + ActionSeqlock(descriptor); + JITCodeEntry* next = entry->next_.load(std::memory_order_relaxed); if (entry->prev_ != nullptr) { - entry->prev_->next_ = entry->next_; + entry->prev_->next_.store(next, std::memory_order_relaxed); } else { - descriptor.first_entry_ = entry->next_; + descriptor.head_.store(next, std::memory_order_relaxed); } - if (entry->next_ != nullptr) { - entry->next_->prev_ = entry->prev_; + if (next != nullptr) { + next->prev_ = entry->prev_; } - descriptor.relevant_entry_ = entry; descriptor.action_flag_ = JIT_UNREGISTER_FN; - descriptor.action_timestamp_ = NanoTime(); - UnlockActionCounter(descriptor, old_action_counter); + descriptor.action_timestamp_ = timestamp; + ActionSequnlock(descriptor); (*register_code_ptr)(); + + // Ensure that clear below can not be reordered above the unlock above. + std::atomic_thread_fence(std::memory_order_release); + + // Aggressively clear the entry as an extra check of the synchronisation. + memset(entry, 0, sizeof(*entry)); + delete entry; } diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index ee7d217e8d..f0898f49d3 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -304,20 +304,25 @@ inline bool Class::HasVTable() { return GetVTable() != nullptr || ShouldHaveEmbeddedVTable(); } + template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline int32_t Class::GetVTableLength() { - if (ShouldHaveEmbeddedVTable()) { + if (ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>()) { return GetEmbeddedVTableLength(); } - return GetVTable() != nullptr ? GetVTable()->GetLength() : 0; + return GetVTable<kVerifyFlags, kReadBarrierOption>() != nullptr ? + GetVTable<kVerifyFlags, kReadBarrierOption>()->GetLength() : 0; } + template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline ArtMethod* Class::GetVTableEntry(uint32_t i, PointerSize pointer_size) { - if (ShouldHaveEmbeddedVTable()) { + if (ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>()) { return GetEmbeddedVTableEntry(i, pointer_size); } - auto* vtable = GetVTable(); + auto* vtable = GetVTable<kVerifyFlags, kReadBarrierOption>(); DCHECK(vtable != nullptr); - return vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size); + return vtable->template GetElementPtrSize<ArtMethod*, kVerifyFlags, kReadBarrierOption>(i, pointer_size); } inline int32_t Class::GetEmbeddedVTableLength() { @@ -627,8 +632,10 @@ inline IfTable* Class::GetIfTable() { return ret.Ptr(); } +template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline int32_t Class::GetIfTableCount() { - return GetIfTable()->Count(); + return GetIfTable<kVerifyFlags, kReadBarrierOption>()->Count(); } inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) { @@ -1144,10 +1151,6 @@ inline bool Class::CanAccessMember(ObjPtr<Class> access_to, uint32_t member_flag if (this == access_to) { return true; } - // Do not allow non-boot class path classes access hidden APIs. - if (hiddenapi::ShouldBlockAccessToMember(member_flags, this)) { - return false; - } // Public members are trivially accessible if (member_flags & kAccPublic) { return true; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index ea065676a0..51d1376a3c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -808,8 +808,12 @@ class MANAGED Class FINAL : public Object { static MemberOffset EmbeddedVTableEntryOffset(uint32_t i, PointerSize pointer_size); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> int32_t GetVTableLength() REQUIRES_SHARED(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ArtMethod* GetVTableEntry(uint32_t i, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -941,6 +945,8 @@ class MANAGED Class FINAL : public Object { return (GetAccessFlags() & kAccRecursivelyInitialized) != 0; } + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE int32_t GetIfTableCount() REQUIRES_SHARED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h index 296c163ef7..d72c7866c5 100644 --- a/runtime/mirror/iftable.h +++ b/runtime/mirror/iftable.h @@ -25,8 +25,11 @@ namespace mirror { class MANAGED IfTable FINAL : public ObjectArray<Object> { public: + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE Class* GetInterface(int32_t i) REQUIRES_SHARED(Locks::mutator_lock_) { - Class* interface = GetWithoutChecks((i * kMax) + kInterface)->AsClass(); + Class* interface = + GetWithoutChecks<kVerifyFlags, kReadBarrierOption>((i * kMax) + kInterface)->AsClass(); DCHECK(interface != nullptr); return interface; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 38ca4c9623..718f9176e9 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -25,6 +25,7 @@ #include "base/file_utils.h" #include "base/logging.h" // For VLOG. +#include "base/macros.h" #include "base/os.h" #include "base/stl_util.h" #include "base/utils.h" @@ -1303,18 +1304,49 @@ void OatFileAssistant::GetOptimizationStatus( InstructionSet isa, std::string* out_compilation_filter, std::string* out_compilation_reason) { - // Try to load the oat file as we would do at runtime. - OatFileAssistant oat_file_assistant(filename.c_str(), isa, true /* load_executable */); + // It may not be possible to load an oat file executable (e.g., selinux restrictions). Load + // non-executable and check the status manually. + OatFileAssistant oat_file_assistant(filename.c_str(), isa, false /* load_executable */); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); if (oat_file == nullptr) { *out_compilation_filter = "run-from-apk"; *out_compilation_reason = "unknown"; - } else { - *out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter()); - const char* reason = oat_file->GetCompilationReason(); - *out_compilation_reason = reason == nullptr ? "unknown" : reason; + return; } + + OatStatus status = oat_file_assistant.GivenOatFileStatus(*oat_file); + const char* reason = oat_file->GetCompilationReason(); + *out_compilation_reason = reason == nullptr ? "unknown" : reason; + switch (status) { + case OatStatus::kOatUpToDate: + *out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter()); + return; + + case kOatCannotOpen: // This should never happen, but be robust. + *out_compilation_filter = "error"; + *out_compilation_reason = "error"; + return; + + // kOatBootImageOutOfDate - The oat file is up to date with respect to the + // dex file, but is out of date with respect to the boot image. + case kOatBootImageOutOfDate: + FALLTHROUGH_INTENDED; + case kOatDexOutOfDate: + if (oat_file_assistant.HasOriginalDexFiles()) { + *out_compilation_filter = "run-from-apk-fallback"; + } else { + *out_compilation_filter = "run-from-vdex-fallback"; + } + return; + + case kOatRelocationOutOfDate: + // On relocation-out-of-date, we'd run the dex code. + *out_compilation_filter = "run-from-vdex-fallback"; + return; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 77e1f2ccfe..326fcbc1fe 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -97,7 +97,8 @@ class VdexFile { // The format version of the dex section header and the dex section, containing // both the dex code and the quickening data. - static constexpr uint8_t kDexSectionVersion[] = { '0', '0', '1', '\0' }; + // Last update: Add owned section for CompactDex. + static constexpr uint8_t kDexSectionVersion[] = { '0', '0', '2', '\0' }; // If the .vdex file has no dex section (hence no dex code nor quickening data), // we encode this magic version. diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index 445a6ff7de..4b92c56b79 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -49,7 +49,7 @@ inline mirror::DexCache* MethodVerifier::GetDexCache() { } inline ArtMethod* MethodVerifier::GetMethod() const { - return mirror_method_; + return method_being_verified_; } inline MethodReference MethodVerifier::GetMethodReference() const { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 2a2afc4a27..b07001e595 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -56,6 +56,7 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" +#include "vdex_file.h" #include "verifier_compiler_binding.h" #include "verifier_deps.h" @@ -565,7 +566,7 @@ MethodVerifier::MethodVerifier(Thread* self, reg_table_(allocator_), work_insn_idx_(dex::kDexNoIndex), dex_method_idx_(dex_method_idx), - mirror_method_(method), + method_being_verified_(method), method_access_flags_(method_access_flags), return_type_(nullptr), dex_file_(dex_file), @@ -643,87 +644,6 @@ void MethodVerifier::FindLocksAtDexPc() { } } -ArtField* MethodVerifier::FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc) { - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader())); - MethodVerifier verifier(hs.Self(), - m->GetDexFile(), - dex_cache, - class_loader, - m->GetClassDef(), - m->GetCodeItem(), - m->GetDexMethodIndex(), - m, - m->GetAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - false /* need_precise_constants */, - false /* verify_to_dump */, - true /* allow_thread_suspension */); - return verifier.FindAccessedFieldAtDexPc(dex_pc); -} - -ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { - CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. - - // Strictly speaking, we ought to be able to get away with doing a subset of the full method - // verification. In practice, the phase we want relies on data structures set up by all the - // earlier passes, so we just run the full method verification and bail out early when we've - // got what we wanted. - bool success = Verify(); - if (!success) { - return nullptr; - } - RegisterLine* register_line = reg_table_.GetLine(dex_pc); - if (register_line == nullptr) { - return nullptr; - } - const Instruction* inst = &code_item_accessor_.InstructionAt(dex_pc); - return GetQuickFieldAccess(inst, register_line); -} - -ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc) { - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader())); - MethodVerifier verifier(hs.Self(), - m->GetDexFile(), - dex_cache, - class_loader, - m->GetClassDef(), - m->GetCodeItem(), - m->GetDexMethodIndex(), - m, - m->GetAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - false /* need_precise_constants */, - false /* verify_to_dump */, - true /* allow_thread_suspension */); - return verifier.FindInvokedMethodAtDexPc(dex_pc); -} - -ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { - CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. - - // Strictly speaking, we ought to be able to get away with doing a subset of the full method - // verification. In practice, the phase we want relies on data structures set up by all the - // earlier passes, so we just run the full method verification and bail out early when we've - // got what we wanted. - bool success = Verify(); - if (!success) { - return nullptr; - } - RegisterLine* register_line = reg_table_.GetLine(dex_pc); - if (register_line == nullptr) { - return nullptr; - } - const Instruction* inst = &code_item_accessor_.InstructionAt(dex_pc); - const bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); - return GetQuickInvokedMethod(inst, register_line, is_range, false); -} - bool MethodVerifier::Verify() { // Some older code doesn't correctly mark constructors as such. Test for this case by looking at // the name. @@ -4414,62 +4334,24 @@ bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) return true; } -ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, RegisterLine* reg_line, - bool is_range, bool allow_failure) { +ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, bool is_range) { if (is_range) { DCHECK_EQ(inst->Opcode(), Instruction::INVOKE_VIRTUAL_RANGE_QUICK); } else { DCHECK_EQ(inst->Opcode(), Instruction::INVOKE_VIRTUAL_QUICK); } - const RegType& actual_arg_type = reg_line->GetInvocationThis(this, inst, allow_failure); - if (!actual_arg_type.HasClass()) { - VLOG(verifier) << "Failed to get mirror::Class* from '" << actual_arg_type << "'"; - return nullptr; - } - mirror::Class* klass = actual_arg_type.GetClass(); - mirror::Class* dispatch_class; - if (klass->IsInterface()) { - // Derive Object.class from Class.class.getSuperclass(). - mirror::Class* object_klass = klass->GetClass()->GetSuperClass(); - if (FailOrAbort(object_klass->IsObjectClass(), - "Failed to find Object class in quickened invoke receiver", - work_insn_idx_)) { - return nullptr; - } - dispatch_class = object_klass; - } else { - dispatch_class = klass; - } - if (!dispatch_class->HasVTable()) { - FailOrAbort(allow_failure, - "Receiver class has no vtable for quickened invoke at ", - work_insn_idx_); - return nullptr; - } - uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - auto* cl = Runtime::Current()->GetClassLinker(); - auto pointer_size = cl->GetImagePointerSize(); - if (static_cast<int32_t>(vtable_index) >= dispatch_class->GetVTableLength()) { - FailOrAbort(allow_failure, - "Receiver class has not enough vtable slots for quickened invoke at ", - work_insn_idx_); - return nullptr; - } - ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index, pointer_size); - if (self_->IsExceptionPending()) { - FailOrAbort(allow_failure, - "Unexpected exception pending for quickened invoke at ", - work_insn_idx_); - return nullptr; - } - return res_method; + + DCHECK(method_being_verified_ != nullptr); + uint16_t method_idx = method_being_verified_->GetIndexFromQuickening(work_insn_idx_); + CHECK_NE(method_idx, DexFile::kDexNoIndex16); + return ResolveMethodAndCheckAccess(method_idx, METHOD_VIRTUAL); } ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, bool is_range) { DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_) << dex_file_->PrettyMethod(dex_method_idx_, true) << "@" << work_insn_idx_; - ArtMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(), is_range, false); + ArtMethod* res_method = GetQuickInvokedMethod(inst, is_range); if (res_method == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); return nullptr; @@ -5090,22 +4972,17 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } } -ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) { - DCHECK(IsInstructionIGetQuickOrIPutQuick(inst->Opcode())) << inst->Opcode(); - const RegType& object_type = reg_line->GetRegisterType(this, inst->VRegB_22c()); - if (!object_type.HasClass()) { - VLOG(verifier) << "Failed to get mirror::Class* from '" << object_type << "'"; - return nullptr; - } - uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c()); - ArtField* const f = ArtField::FindInstanceFieldWithOffset(object_type.GetClass(), field_offset); - if (f == nullptr) { - VLOG(verifier) << "Failed to find instance field at offset '" << field_offset - << "' from '" << mirror::Class::PrettyDescriptor(object_type.GetClass()) << "'"; - } else { - DCHECK_EQ(f->GetOffset().Uint32Value(), field_offset); +ArtField* MethodVerifier::GetQuickAccessedField() { + DCHECK(method_being_verified_ != nullptr); + uint16_t field_idx = method_being_verified_->GetIndexFromQuickening(work_insn_idx_); + CHECK_NE(field_idx, DexFile::kDexNoIndex16); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_); + if (field == nullptr) { + DCHECK(self_->IsExceptionPending()); + self_->ClearException(); } - return f; + return field; } template <MethodVerifier::FieldAccessType kAccType> @@ -5113,7 +4990,7 @@ void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegTy bool is_primitive) { DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); - ArtField* field = GetQuickFieldAccess(inst, work_line_.get()); + ArtField* field = GetQuickAccessedField(); if (field == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); return; @@ -5318,12 +5195,12 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() { const RegType& MethodVerifier::GetMethodReturnType() { if (return_type_ == nullptr) { - if (mirror_method_ != nullptr) { + if (method_being_verified_ != nullptr) { ObjPtr<mirror::Class> return_type_class = can_load_classes_ - ? mirror_method_->ResolveReturnType() - : mirror_method_->LookupResolvedReturnType(); + ? method_being_verified_->ResolveReturnType() + : method_being_verified_->LookupResolvedReturnType(); if (return_type_class != nullptr) { - return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(), + return_type_ = &FromClass(method_being_verified_->GetReturnTypeDescriptor(), return_type_class.Ptr(), return_type_class->CannotBeAssignedFromOtherTypes()); } else { @@ -5347,8 +5224,8 @@ const RegType& MethodVerifier::GetDeclaringClass() { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - if (mirror_method_ != nullptr) { - mirror::Class* klass = mirror_method_->GetDeclaringClass(); + if (method_being_verified_ != nullptr) { + mirror::Class* klass = method_being_verified_->GetDeclaringClass(); declaring_class_ = &FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } else { declaring_class_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 9a94297942..4c9518b0ec 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -162,20 +162,11 @@ class MethodVerifier { }; // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding // to the locks held at 'dex_pc' in method 'm'. + // Note: this is the only situation where the verifier will visit quickened instructions. static void FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, std::vector<DexLockInfo>* monitor_enter_dex_pcs) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the accessed field corresponding to the quick instruction's field - // offset at 'dex_pc' in method 'm'. - static ArtField* FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Returns the invoked method corresponding to the quick instruction's vtable - // index at 'dex_pc' in method 'm'. - static ArtMethod* FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - static void Init() REQUIRES_SHARED(Locks::mutator_lock_); static void Shutdown(); @@ -206,7 +197,7 @@ class MethodVerifier { ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index); mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); mirror::DexCache* GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* GetMethod() const REQUIRES_SHARED(Locks::mutator_lock_); + ArtMethod* GetMethod() const; MethodReference GetMethodReference() const; uint32_t GetAccessFlags() const; bool HasCheckCasts() const; @@ -219,13 +210,11 @@ class MethodVerifier { const RegType& ResolveCheckedClass(dex::TypeIndex class_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method of a quick invoke or null if it cannot be found. - ArtMethod* GetQuickInvokedMethod(const Instruction* inst, RegisterLine* reg_line, - bool is_range, bool allow_failure) + ArtMethod* GetQuickInvokedMethod(const Instruction* inst, bool is_range) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the access field of a quick field access (iget/iput-quick) or null // if it cannot be found. - ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) - REQUIRES_SHARED(Locks::mutator_lock_); + ArtField* GetQuickAccessedField() REQUIRES_SHARED(Locks::mutator_lock_); uint32_t GetEncounteredFailureTypes() { return encountered_failure_types_; @@ -332,15 +321,6 @@ class MethodVerifier { void FindLocksAtDexPc() REQUIRES_SHARED(Locks::mutator_lock_); - ArtField* FindAccessedFieldAtDexPc(uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindInvokedMethodAtDexPc(uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_); - - SafeMap<uint32_t, std::set<uint32_t>>& FindStringInitMap() - REQUIRES_SHARED(Locks::mutator_lock_); - /* * Compute the width of the instruction at each address in the instruction stream, and store it in * insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch @@ -745,8 +725,7 @@ class MethodVerifier { RegisterLineArenaUniquePtr saved_line_; const uint32_t dex_method_idx_; // The method we're working on. - // Its object representation if known. - ArtMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_); + ArtMethod* method_being_verified_; // Its ArtMethod representation if known. const uint32_t method_access_flags_; // Method's access flags. const RegType* return_type_; // Lazily computed return type of the method. const DexFile* const dex_file_; // The dex file containing the method. diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index b91d983e75..49db0c82b5 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -128,7 +128,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( // "mini-debug-info" does not include parameters to save space. std::vector<std::string> seq = { "Java_Main_unwindInProcess", // This function. - "Main.unwindInProcess", // The corresponding Java native method frame. "java.util.Arrays.binarySearch0", // Framework method. "Base.runBase", // Method in other dex file. "Main.main" // The Java entry method. @@ -225,11 +224,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( // See comment in unwindInProcess for non-exact stack matching. // "mini-debug-info" does not include parameters to save space. std::vector<std::string> seq = { - // "Java_Main_sleep", // The sleep function being executed in the - // other runtime. - // Note: For some reason, the name isn't - // resolved, so don't look for it right now. - "Main.sleep", // The corresponding Java native method frame. + "Java_Main_sleep", // The sleep function in the other process. "java.util.Arrays.binarySearch0", // Framework method. "Base.runBase", // Method in other dex file. "Main.main" // The Java entry method. diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc new file mode 100644 index 0000000000..4be3456e3d --- /dev/null +++ b/test/616-cha-unloading/cha_unload.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 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 "jni.h" + +#include <iostream> + +#include "art_method.h" +#include "jit/jit.h" +#include "linear_alloc.h" +#include "nativehelper/ScopedUtfChars.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { +namespace { + +extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env, + jclass, + jobject java_method) { + ScopedObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, java_method); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(method)); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_reuseArenaOfMethod(JNIEnv*, + jclass, + jlong art_method) { + // Create a new allocation and use it to request a specified amount of arenas. + // Hopefully one of them is a reused one, the one that covers the art_method pointer. + std::unique_ptr<LinearAlloc> alloc(Runtime::Current()->CreateLinearAlloc()); + do { + // Ask for a byte - it's sufficient to get an arena and not have issues with size. + alloc->Alloc(Thread::Current(), 1); + } while (!alloc->Contains(reinterpret_cast<void*>(static_cast<uintptr_t>(art_method)))); +} + +} // namespace +} // namespace art diff --git a/test/616-cha-unloading/expected.txt b/test/616-cha-unloading/expected.txt new file mode 100644 index 0000000000..77a1486479 --- /dev/null +++ b/test/616-cha-unloading/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +Done diff --git a/test/616-cha-unloading/info.txt b/test/616-cha-unloading/info.txt new file mode 100644 index 0000000000..563380b6b2 --- /dev/null +++ b/test/616-cha-unloading/info.txt @@ -0,0 +1 @@ +Test that class unloading updated single implementations. diff --git a/test/616-cha-unloading/run b/test/616-cha-unloading/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-unloading/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-unloading/src-art/AbstractCHATester.java b/test/616-cha-unloading/src-art/AbstractCHATester.java new file mode 100644 index 0000000000..e11094584a --- /dev/null +++ b/test/616-cha-unloading/src-art/AbstractCHATester.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 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. + */ + +public abstract class AbstractCHATester { + public abstract void lonelyMethod(); +} diff --git a/test/616-cha-unloading/src-art/Main.java b/test/616-cha-unloading/src-art/Main.java new file mode 100644 index 0000000000..effa315e25 --- /dev/null +++ b/test/616-cha-unloading/src-art/Main.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 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.ref.WeakReference; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class Main { + static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/616-cha-unloading-ex.jar"; + static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); + static Constructor<? extends ClassLoader> sConstructor; + + private static class CHAUnloaderRetType { + private CHAUnloaderRetType(WeakReference<ClassLoader> cl, + AbstractCHATester obj, + long methodPtr) { + this.cl = cl; + this.obj = obj; + this.methodPtr = methodPtr; + } + public WeakReference<ClassLoader> cl; + public AbstractCHATester obj; + public long methodPtr; + } + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + Class<ClassLoader> pathClassLoader = (Class<ClassLoader>) Class.forName("dalvik.system.PathClassLoader"); + sConstructor = + pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); + + testUnload(); + } + + private static void testUnload() throws Exception { + // Load a concrete class, then unload it. Get a deleted ArtMethod to test if it'll be inlined. + CHAUnloaderRetType result = doUnloadLoader(); + WeakReference<ClassLoader> loader = result.cl; + long methodPtr = result.methodPtr; + // Check that the classloader is indeed unloaded. + if (loader.get() != null) { + throw new Error("Expected class loader to be unloaded"); + } + + // Reuse the linear alloc used by the unloaded class loader. + reuseArenaOfMethod(methodPtr); + + // Try to JIT-compile under dangerous conditions. + ensureJitCompiled(Main.class, "targetMethodForJit"); + System.out.println("Done"); + } + + private static void doUnloading() { + // Do multiple GCs to prevent rare flakiness if some other thread is keeping the + // classloader live. + for (int i = 0; i < 5; ++i) { + Runtime.getRuntime().gc(); + } + } + + private static CHAUnloaderRetType setupLoader() + throws Exception { + ClassLoader loader = sConstructor.newInstance( + DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); + Class<?> concreteCHATester = loader.loadClass("ConcreteCHATester"); + + // Preemptively compile methods to prevent delayed JIT tasks from blocking the unloading. + ensureJitCompiled(concreteCHATester, "<init>"); + ensureJitCompiled(concreteCHATester, "lonelyMethod"); + + Object obj = concreteCHATester.newInstance(); + Method lonelyMethod = concreteCHATester.getDeclaredMethod("lonelyMethod"); + + // Get a pointer to a region that shall be not used after the unloading. + long artMethod = getArtMethod(lonelyMethod); + + AbstractCHATester ret = null; + return new CHAUnloaderRetType(new WeakReference(loader), ret, artMethod); + } + + private static CHAUnloaderRetType targetMethodForJit(int mode) + throws Exception { + CHAUnloaderRetType ret = new CHAUnloaderRetType(null, null, 0); + if (mode == 0) { + ret = setupLoader(); + } else if (mode == 1) { + // This branch is not supposed to be executed. It shall trigger "lonelyMethod" inlining + // during jit compilation of "targetMethodForJit". + ret = setupLoader(); + AbstractCHATester obj = ret.obj; + obj.lonelyMethod(); + } + return ret; + } + + private static CHAUnloaderRetType doUnloadLoader() + throws Exception { + CHAUnloaderRetType result = targetMethodForJit(0); + doUnloading(); + return result; + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native long getArtMethod(Object javaMethod); + private static native void reuseArenaOfMethod(long artMethod); +} diff --git a/test/616-cha-unloading/src-ex/AbstractCHATester.java b/test/616-cha-unloading/src-ex/AbstractCHATester.java new file mode 100644 index 0000000000..e11094584a --- /dev/null +++ b/test/616-cha-unloading/src-ex/AbstractCHATester.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 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. + */ + +public abstract class AbstractCHATester { + public abstract void lonelyMethod(); +} diff --git a/test/616-cha-unloading/src-ex/ConcreteCHATester.java b/test/616-cha-unloading/src-ex/ConcreteCHATester.java new file mode 100644 index 0000000000..ee2be9c3f5 --- /dev/null +++ b/test/616-cha-unloading/src-ex/ConcreteCHATester.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 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. + */ + +public class ConcreteCHATester extends AbstractCHATester { + public void lonelyMethod() {} +} diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java index 4e2b241fd7..ff6e335b7f 100644 --- a/test/623-checker-loop-regressions/src/Main.java +++ b/test/623-checker-loop-regressions/src/Main.java @@ -584,6 +584,18 @@ public class Main { s24 + s25 + s26 + s27 + s28 + s29 + s30 + s31; } + public static int reductionIntoReplication() { + int[] a = { 1, 2, 3, 4 }; + int x = 0; + for (int i = 0; i < 4; i++) { + x += a[i]; + } + for (int i = 0; i < 4; i++) { + a[i] = x; + } + return a[3]; + } + public static void main(String[] args) { System.loadLibrary(args[0]); @@ -767,6 +779,8 @@ public class Main { expectEquals(85800, reduction32Values(a1, a2, a3, a4)); } + expectEquals(10, reductionIntoReplication()); + System.out.println("passed"); } diff --git a/test/674-hiddenapi/api-blacklist.txt b/test/674-hiddenapi/api-blacklist.txt index d43360c62f..4a67fb8ebf 100644 --- a/test/674-hiddenapi/api-blacklist.txt +++ b/test/674-hiddenapi/api-blacklist.txt @@ -1,9 +1,11 @@ LNullaryConstructorBlacklist;-><init>()V LParentClass;->fieldPublicBlacklist:I +LParentClass;->fieldPublicBlacklistB:I LParentClass;->fieldPackageBlacklist:I LParentClass;->fieldProtectedBlacklist:I LParentClass;->fieldPrivateBlacklist:I LParentClass;->fieldPublicStaticBlacklist:I +LParentClass;->fieldPublicStaticBlacklistB:I LParentClass;->fieldPackageStaticBlacklist:I LParentClass;->fieldProtectedStaticBlacklist:I LParentClass;->fieldPrivateStaticBlacklist:I diff --git a/test/674-hiddenapi/api-dark-greylist.txt b/test/674-hiddenapi/api-dark-greylist.txt index d0f35f64bc..e010a0a07f 100644 --- a/test/674-hiddenapi/api-dark-greylist.txt +++ b/test/674-hiddenapi/api-dark-greylist.txt @@ -1,9 +1,11 @@ LNullaryConstructorDarkGreylist;-><init>()V LParentClass;->fieldPublicDarkGreylist:I +LParentClass;->fieldPublicDarkGreylistB:I LParentClass;->fieldPackageDarkGreylist:I LParentClass;->fieldProtectedDarkGreylist:I LParentClass;->fieldPrivateDarkGreylist:I LParentClass;->fieldPublicStaticDarkGreylist:I +LParentClass;->fieldPublicStaticDarkGreylistB:I LParentClass;->fieldPackageStaticDarkGreylist:I LParentClass;->fieldProtectedStaticDarkGreylist:I LParentClass;->fieldPrivateStaticDarkGreylist:I diff --git a/test/674-hiddenapi/api-light-greylist.txt b/test/674-hiddenapi/api-light-greylist.txt index 2809025cfd..4be793fd0c 100644 --- a/test/674-hiddenapi/api-light-greylist.txt +++ b/test/674-hiddenapi/api-light-greylist.txt @@ -1,9 +1,11 @@ LNullaryConstructorLightGreylist;-><init>()V LParentClass;->fieldPublicLightGreylist:I +LParentClass;->fieldPublicLightGreylistB:I LParentClass;->fieldPackageLightGreylist:I LParentClass;->fieldProtectedLightGreylist:I LParentClass;->fieldPrivateLightGreylist:I LParentClass;->fieldPublicStaticLightGreylist:I +LParentClass;->fieldPublicStaticLightGreylistB:I LParentClass;->fieldPackageStaticLightGreylist:I LParentClass;->fieldProtectedStaticLightGreylist:I LParentClass;->fieldPrivateStaticLightGreylist:I diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index babd88359b..8cd237ab6f 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -120,9 +120,12 @@ public class ChildClass { // Check whether one can use a class constructor. checkConstructor(ParentClass.class, visibility, hiddenness, expected); - // Check whether you can use an interface default method. + // Check whether one can use an interface default method. String name = "method" + visibility.name() + "Default" + hiddenness.name(); checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected); + + // Check whether one can override this method. + checkOverriding(suffix, isStatic, visibility, expected); } // Test whether static linking succeeds. @@ -389,7 +392,7 @@ public class ChildClass { private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour) throws Exception { boolean canAccess = (behaviour != Behaviour.Denied); - boolean setsWarning = false; // we do not set the flag in verifier or at runtime + boolean setsWarning = (behaviour == Behaviour.Warning); clearWarning(); if (Linking.canAccess(className, takesParameter) != canAccess) { @@ -403,6 +406,37 @@ public class ChildClass { } } + private static void checkOverriding(String suffix, + boolean isStatic, + Visibility visibility, + Behaviour behaviour) throws Exception { + if (isStatic || visibility == Visibility.Private) { + // Does not make sense to override a static or private method. + return; + } + + // The classes are in the same package, but will be able to access each + // other only if loaded with the same class loader, here the boot class loader. + boolean canAccess = (visibility != Visibility.Package) || (isParentInBoot && isChildInBoot); + boolean setsWarning = false; // warnings may be set during vtable linking + + String methodName = "callMethod" + visibility.name() + suffix; + + // Force the test class to link its vtable, which may cause warnings, before + // the actual test. + new OverrideClass().methodPublicWhitelist(); + + clearWarning(); + if (Linking.canOverride(methodName) != canAccess) { + throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") + + "be able to override " + methodName + "." + + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); + } + if (canAccess && hasPendingWarning() != setsWarning) { + throwWarningException(ParentClass.class, methodName, false, "static linking", setsWarning); + } + } + private static void throwDiscoveryException(Class<?> klass, String name, boolean isField, String fn, boolean canAccess) { throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() + diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java index c6735d85fe..b416250953 100644 --- a/test/674-hiddenapi/src-ex/Linking.java +++ b/test/674-hiddenapi/src-ex/Linking.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; public class Linking { @@ -27,13 +28,23 @@ public class Linking { } return true; } catch (InvocationTargetException ex) { - if (ex.getCause() instanceof IllegalAccessError) { + if (ex.getCause() instanceof NoSuchFieldError || ex.getCause() instanceof NoSuchMethodError) { return false; } else { throw ex; } } } + + public static boolean canOverride(String methodName) throws Exception { + // ParentClass returns only positive numbers, OverrideClass only negative. + // This way we can tell if OverrideClass managed to override the original + // method or not. + Method method = ParentClass.class.getDeclaredMethod(methodName); + int result1 = (int) method.invoke(new ParentClass()); + int result2 = (int) method.invoke(new OverrideClass()); + return (result1 > 0) && (result2 < 0); + } } // INSTANCE FIELD GET @@ -66,25 +77,29 @@ class LinkFieldGetBlacklist { class LinkFieldSetWhitelist { public static void access(int x) { - new ParentClass().fieldPublicWhitelist = x; + // Need to use a different field from the getter to bypass DexCache. + new ParentClass().fieldPublicWhitelistB = x; } } class LinkFieldSetLightGreylist { public static void access(int x) { - new ParentClass().fieldPublicLightGreylist = x; + // Need to use a different field from the getter to bypass DexCache. + new ParentClass().fieldPublicLightGreylistB = x; } } class LinkFieldSetDarkGreylist { public static void access(int x) { - new ParentClass().fieldPublicDarkGreylist = x; + // Need to use a different field from the getter to bypass DexCache. + new ParentClass().fieldPublicDarkGreylistB = x; } } class LinkFieldSetBlacklist { public static void access(int x) { - new ParentClass().fieldPublicBlacklist = x; + // Need to use a different field from the getter to bypass DexCache. + new ParentClass().fieldPublicBlacklistB = x; } } @@ -118,25 +133,29 @@ class LinkFieldGetStaticBlacklist { class LinkFieldSetStaticWhitelist { public static void access(int x) { - ParentClass.fieldPublicStaticWhitelist = x; + // Need to use a different field from the getter to bypass DexCache. + ParentClass.fieldPublicStaticWhitelistB = x; } } class LinkFieldSetStaticLightGreylist { public static void access(int x) { - ParentClass.fieldPublicStaticLightGreylist = x; + // Need to use a different field from the getter to bypass DexCache. + ParentClass.fieldPublicStaticLightGreylistB = x; } } class LinkFieldSetStaticDarkGreylist { public static void access(int x) { - ParentClass.fieldPublicStaticDarkGreylist = x; + // Need to use a different field from the getter to bypass DexCache. + ParentClass.fieldPublicStaticDarkGreylistB = x; } } class LinkFieldSetStaticBlacklist { public static void access(int x) { - ParentClass.fieldPublicStaticBlacklist = x; + // Need to use a different field from the getter to bypass DexCache. + ParentClass.fieldPublicStaticBlacklistB = x; } } diff --git a/test/674-hiddenapi/src-ex/OverrideClass.java b/test/674-hiddenapi/src-ex/OverrideClass.java new file mode 100644 index 0000000000..1f1f4d6aac --- /dev/null +++ b/test/674-hiddenapi/src-ex/OverrideClass.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 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. + */ + +public class OverrideClass extends ParentClass { + + @Override public int methodPublicWhitelist() { return -411; } + @Override int methodPackageWhitelist() { return -412; } + @Override protected int methodProtectedWhitelist() { return -413; } + + @Override public int methodPublicLightGreylist() { return -421; } + @Override int methodPackageLightGreylist() { return -422; } + @Override protected int methodProtectedLightGreylist() { return -423; } + + @Override public int methodPublicDarkGreylist() { return -431; } + @Override int methodPackageDarkGreylist() { return -432; } + @Override protected int methodProtectedDarkGreylist() { return -433; } + + @Override public int methodPublicBlacklist() { return -441; } + @Override int methodPackageBlacklist() { return -442; } + @Override protected int methodProtectedBlacklist() { return -443; } + +} diff --git a/test/674-hiddenapi/src/ParentClass.java b/test/674-hiddenapi/src/ParentClass.java index edad02dc2c..07e84ccad5 100644 --- a/test/674-hiddenapi/src/ParentClass.java +++ b/test/674-hiddenapi/src/ParentClass.java @@ -23,21 +23,25 @@ public class ParentClass { int fieldPackageWhitelist = 212; protected int fieldProtectedWhitelist = 213; private int fieldPrivateWhitelist = 214; + public int fieldPublicWhitelistB = 215; public int fieldPublicLightGreylist = 221; int fieldPackageLightGreylist = 222; protected int fieldProtectedLightGreylist = 223; private int fieldPrivateLightGreylist = 224; + public int fieldPublicLightGreylistB = 225; public int fieldPublicDarkGreylist = 231; int fieldPackageDarkGreylist = 232; protected int fieldProtectedDarkGreylist = 233; private int fieldPrivateDarkGreylist = 234; + public int fieldPublicDarkGreylistB = 235; public int fieldPublicBlacklist = 241; int fieldPackageBlacklist = 242; protected int fieldProtectedBlacklist = 243; private int fieldPrivateBlacklist = 244; + public int fieldPublicBlacklistB = 245; // STATIC FIELD @@ -45,21 +49,25 @@ public class ParentClass { static int fieldPackageStaticWhitelist = 112; protected static int fieldProtectedStaticWhitelist = 113; private static int fieldPrivateStaticWhitelist = 114; + public static int fieldPublicStaticWhitelistB = 115; public static int fieldPublicStaticLightGreylist = 121; static int fieldPackageStaticLightGreylist = 122; protected static int fieldProtectedStaticLightGreylist = 123; private static int fieldPrivateStaticLightGreylist = 124; + public static int fieldPublicStaticLightGreylistB = 125; public static int fieldPublicStaticDarkGreylist = 131; static int fieldPackageStaticDarkGreylist = 132; protected static int fieldProtectedStaticDarkGreylist = 133; private static int fieldPrivateStaticDarkGreylist = 134; + public static int fieldPublicStaticDarkGreylistB = 135; public static int fieldPublicStaticBlacklist = 141; static int fieldPackageStaticBlacklist = 142; protected static int fieldProtectedStaticBlacklist = 143; private static int fieldPrivateStaticBlacklist = 144; + public static int fieldPublicStaticBlacklistB = 145; // INSTANCE METHOD @@ -130,4 +138,23 @@ public class ParentClass { ParentClass(float x, char y) {} protected ParentClass(long x, char y) {} private ParentClass(double x, char y) {} + + // HELPERS + + public int callMethodPublicWhitelist() { return methodPublicWhitelist(); } + public int callMethodPackageWhitelist() { return methodPackageWhitelist(); } + public int callMethodProtectedWhitelist() { return methodProtectedWhitelist(); } + + public int callMethodPublicLightGreylist() { return methodPublicLightGreylist(); } + public int callMethodPackageLightGreylist() { return methodPackageLightGreylist(); } + public int callMethodProtectedLightGreylist() { return methodProtectedLightGreylist(); } + + public int callMethodPublicDarkGreylist() { return methodPublicDarkGreylist(); } + public int callMethodPackageDarkGreylist() { return methodPackageDarkGreylist(); } + public int callMethodProtectedDarkGreylist() { return methodProtectedDarkGreylist(); } + + public int callMethodPublicBlacklist() { return methodPublicBlacklist(); } + public int callMethodPackageBlacklist() { return methodPackageBlacklist(); } + public int callMethodProtectedBlacklist() { return methodProtectedBlacklist(); } + } diff --git a/test/678-checker-simd-saturation/expected.txt b/test/678-checker-simd-saturation/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/678-checker-simd-saturation/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/678-checker-simd-saturation/info.txt b/test/678-checker-simd-saturation/info.txt new file mode 100644 index 0000000000..ab7a80241d --- /dev/null +++ b/test/678-checker-simd-saturation/info.txt @@ -0,0 +1 @@ +Functional tests on saturation arithmetic vectorization. diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java new file mode 100644 index 0000000000..33a6f5ec80 --- /dev/null +++ b/test/678-checker-simd-saturation/src/Main.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2018 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. + */ + +/** + * Functional tests for saturation aritmethic vectorization. + */ +public class Main { + + // + // Direct min-max. + // + + /// CHECK-START: void Main.satAddUByte(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-DAG: <<Clip:i\d+>> IntConstant 255 loop:none + /// CHECK-DAG: <<Get1:a\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:a\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Add>>,<<Clip>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddUByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + public static void satAddUByte(byte[] a, byte[] b, byte[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (byte) Math.min((a[i] & 0xff) + (b[i] & 0xff), 255); + } + } + + /// CHECK-START: void Main.satAddSByte(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -128 loop:none + /// CHECK-DAG: <<Clp2:i\d+>> IntConstant 127 loop:none + /// CHECK-DAG: <<Get1:b\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:b\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Add>>,<<Clp2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<Clp1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddSByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + public static void satAddSByte(byte[] a, byte[] b, byte[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (byte) Math.max(Math.min(a[i] + b[i], 127), -128); + } + } + + /// CHECK-START: void Main.satAddUShort(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <<Clip:i\d+>> IntConstant 65535 loop:none + /// CHECK-DAG: <<Get1:c\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:c\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Add>>,<<Clip>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddUShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + public static void satAddUShort(short[] a, short[] b, short[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (short) Math.min((a[i] & 0xffff) + (b[i] & 0xffff), 65535); + } + } + + /// CHECK-START: void Main.satAddSShort(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768 loop:none + /// CHECK-DAG: <<Clp2:i\d+>> IntConstant 32767 loop:none + /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Add>>,<<Clp2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<Clp1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddSShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + public static void satAddSShort(short[] a, short[] b, short[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (short) Math.max(Math.min(a[i] + b[i], 32767), -32768); + } + } + + /// CHECK-START: void Main.satSubUByte(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-DAG: <<Clip:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Get1:a\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:a\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Sub>>,<<Clip>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubUByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none + public static void satSubUByte(byte[] a, byte[] b, byte[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (byte) Math.max((a[i] & 0xff) - (b[i] & 0xff), 0); + } + } + + /// CHECK-START: void Main.satSubSByte(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -128 loop:none + /// CHECK-DAG: <<Clp2:i\d+>> IntConstant 127 loop:none + /// CHECK-DAG: <<Get1:b\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:b\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Sub>>,<<Clp2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<Clp1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubSByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none + public static void satSubSByte(byte[] a, byte[] b, byte[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (byte) Math.max(Math.min(a[i] - b[i], 127), -128); + } + } + + /// CHECK-START: void Main.satSubUShort(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <<Clip:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Get1:c\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:c\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Sub>>,<<Clip>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubUShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none + public static void satSubUShort(short[] a, short[] b, short[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (short) Math.max((a[i] & 0xffff) - (b[i] & 0xffff), 0); + } + } + + /// CHECK-START: void Main.satSubSShort(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768 loop:none + /// CHECK-DAG: <<Clp2:i\d+>> IntConstant 32767 loop:none + /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Sub>>,<<Clp2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<Clp1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubSShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none + public static void satSubSShort(short[] a, short[] b, short[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + c[i] = (short) Math.max(Math.min(a[i] - b[i], 32767), -32768); + } + } + + // + // Alternatives. + // + + /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768 loop:none + /// CHECK-DAG: <<Clp2:i\d+>> IntConstant 32767 loop:none + /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Add>>,<<Clp2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<Clp1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAlt1(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + public static void satAlt1(short[] a, short[] b, short[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + int s = a[i] + b[i]; + if (s > 32767) { + s = 32767; + } + if (s < -32768) { + s = -32768; + } + c[i] = (short) s; + } + } + + // TODO: recognize the more common if-else too. + public static void satAlt2(short[] a, short[] b, short[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + int s = a[i] + b[i]; + if (s > 32767) { + s = 32767; + } else if (s < -32768) { + s = -32768; + } + c[i] = (short) s; + } + } + + // TODO: recognize conditional too. + public static void satAlt3(short[] a, short[] b, short[] c) { + int n = Math.min(a.length, Math.min(b.length, c.length)); + for (int i = 0; i < n; i++) { + int s = a[i] + b[i]; + s = (s > 32767) ? 32767 : ((s < -32768) ? -32768 : s); + c[i] = (short) s; + } + } + + // + // Test drivers. + // + + private static void test08Bit() { + // Use cross-values to test all cases. + int n = 256; + int m = n * n; + int k = 0; + byte[] b1 = new byte[m]; + byte[] b2 = new byte[m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + b1[k] = (byte) i; + b2[k] = (byte) j; + k++; + } + } + // Tests. + byte[] out = new byte[m]; + satAddUByte(b1, b2, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.min((b1[i] & 0xff) + (b2[i] & 0xff), 255); + expectEquals(e, out[i]); + } + satAddSByte( b1, b2, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max(Math.min(b1[i] + b2[i], 127), -128); + expectEquals(e, out[i]); + } + satSubUByte(b1, b2, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max((b1[i] & 0xff) - (b2[i] & 0xff), 0); + expectEquals(e, out[i]); + } + satSubSByte(b1, b2, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max(Math.min(b1[i] - b2[i], 127), -128); + expectEquals(e, out[i]); + } + } + + private static void test16Bit() { + // Use cross-values to test interesting cases. + short[] interesting = { + (short) 0x0000, + (short) 0x0001, + (short) 0x0002, + (short) 0x0003, + (short) 0x0004, + (short) 0x007f, + (short) 0x0080, + (short) 0x00ff, + (short) 0x7f00, + (short) 0x7f7f, + (short) 0x7f80, + (short) 0x7fff, + (short) 0x8000, + (short) 0x807f, + (short) 0x8080, + (short) 0x80ff, + (short) 0xff00, + (short) 0xff7f, + (short) 0xff80, + (short) 0xffff, + }; + int n = interesting.length; + int m = n * n; + short[] s1 = new short[m]; + short[] s2 = new short[m]; + int k = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + s1[k] = interesting[i]; + s2[k] = interesting[j]; + k++; + } + } + // Tests. + short[] out = new short[m]; + satAddUShort(s1, s2, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.min((s1[i] & 0xffff) + (s2[i] & 0xffff), 65535); + expectEquals(e, out[i]); + } + satAddSShort(s1, s2, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768); + expectEquals(e, out[i]); + } + satSubUShort(s1, s2, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max((s1[i] & 0xffff) - (s2[i] & 0xffff), 0); + expectEquals(e, out[i]); + } + satSubSShort(s1, s2, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(Math.min(s1[i] - s2[i], 32767), -32768); + expectEquals(e, out[i]); + } + // Alternatives. + satAlt1(s1, s2, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768); + expectEquals(e, out[i]); + } + satAlt2(s1, s2, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768); + expectEquals(e, out[i]); + } + satAlt3(s1, s2, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768); + expectEquals(e, out[i]); + } + } + + public static void main(String[] args) { + test08Bit(); + test16Bit(); + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/678-quickening/expected.txt b/test/678-quickening/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/678-quickening/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/678-quickening/info.txt b/test/678-quickening/info.txt new file mode 100644 index 0000000000..e08eaeb285 --- /dev/null +++ b/test/678-quickening/info.txt @@ -0,0 +1 @@ +Test for FindLocksAtDexPc running with quickened opcodes. diff --git a/test/678-quickening/run b/test/678-quickening/run new file mode 100644 index 0000000000..0cc87f3168 --- /dev/null +++ b/test/678-quickening/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2018 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.i + +# Run without an app image to prevent the class NotLoaded to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/678-quickening/src-art/Main.java b/test/678-quickening/src-art/Main.java new file mode 100644 index 0000000000..5ae88d60d7 --- /dev/null +++ b/test/678-quickening/src-art/Main.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 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.reflect.Field; +import java.lang.reflect.Method; + +class NotLoaded { + public void foo() {} +} + +public class Main { + public static void main(String[] args) throws Exception { + runTest(null); + } + + // This method being synchronized means the SIGQUIT code in ART will call + // FindLocksAtDexPc (we check for the presence of try blocks and monitor-enter), + // which triggered a DCHECK of an invariant. + public static synchronized void runTest(Object m) throws Exception { + if (m != null) { + // We used to crash while trying to resolve NotLoaded and beint interrupted + // by the SIGQUIT. + if (m instanceof NotLoaded) { + ((NotLoaded)m).foo(); + } + } + SigQuit.doKill(); + // Sleep some time to get the kill while executing this method. + Thread.sleep(2); + System.out.println("Done"); + } + + private final static class SigQuit { + private final static int sigquit; + private final static Method kill; + private final static int pid; + + static { + int pidTemp = -1; + int sigquitTemp = -1; + Method killTemp = null; + + try { + Class<?> osClass = Class.forName("android.system.Os"); + Method getpid = osClass.getDeclaredMethod("getpid"); + pidTemp = (Integer)getpid.invoke(null); + + Class<?> osConstants = Class.forName("android.system.OsConstants"); + Field sigquitField = osConstants.getDeclaredField("SIGQUIT"); + sigquitTemp = (Integer)sigquitField.get(null); + + killTemp = osClass.getDeclaredMethod("kill", int.class, int.class); + } catch (Exception e) { + throw new Error(e); + } + + pid = pidTemp; + sigquit = sigquitTemp; + kill = killTemp; + } + + public static void doKill() throws Exception { + kill.invoke(null, pid, sigquit); + } + } +} diff --git a/test/679-checker-minmax/expected.txt b/test/679-checker-minmax/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/679-checker-minmax/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/679-checker-minmax/info.txt b/test/679-checker-minmax/info.txt new file mode 100644 index 0000000000..4f7b9f52c5 --- /dev/null +++ b/test/679-checker-minmax/info.txt @@ -0,0 +1 @@ +Functional tests on detecting min/max. diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java new file mode 100644 index 0000000000..d016de6686 --- /dev/null +++ b/test/679-checker-minmax/src/Main.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2018 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. + */ + +/** + * Functional tests for detecting min/max. + */ +public class Main { + + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Min:i\d+>> Min + /// CHECK-DAG: Return [<<Min>>] + // + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int min1(int a, int b) { + return a < b ? a : b; + } + + /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Min:i\d+>> Min + /// CHECK-DAG: Return [<<Min>>] + // + /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int min2(int a, int b) { + return a <= b ? a : b; + } + + /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> LessThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Min:i\d+>> Min + /// CHECK-DAG: Return [<<Min>>] + // + /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int min3(int a, int b) { + return a > b ? b : a; + } + + /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Min:i\d+>> Min + /// CHECK-DAG: Return [<<Min>>] + // + /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int min4(int a, int b) { + return a >= b ? b : a; + } + + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Max:i\d+>> Max + /// CHECK-DAG: Return [<<Max>>] + // + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int max1(int a, int b) { + return a < b ? b : a; + } + + /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> GreaterThan [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op2>>,<<Op1>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Max:i\d+>> Max + /// CHECK-DAG: Return [<<Max>>] + // + /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int max2(int a, int b) { + return a <= b ? b : a; + } + + /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> LessThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Max:i\d+>> Max + /// CHECK-DAG: Return [<<Max>>] + // + /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int max3(int a, int b) { + return a > b ? a : b; + } + + /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Cnd:z\d+>> LessThan [<<Op1:i\d+>>,<<Op2:i\d+>>] + /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>] + /// CHECK-DAG: Return [<<Sel>>] + // + /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Max:i\d+>> Max + /// CHECK-DAG: Return [<<Max>>] + // + /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int max4(int a, int b) { + return a >= b ? a : b; + } + + public static void main(String[] args) { + expectEquals(10, min1(10, 20)); + expectEquals(10, min2(10, 20)); + expectEquals(10, min3(10, 20)); + expectEquals(10, min4(10, 20)); + expectEquals(20, max1(10, 20)); + expectEquals(20, max2(10, 20)); + expectEquals(20, max3(10, 20)); + expectEquals(20, max4(10, 20)); + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/979-const-method-handle/build b/test/979-const-method-handle/build index 495557e3df..ce931a96d1 100644..100755 --- a/test/979-const-method-handle/build +++ b/test/979-const-method-handle/build @@ -1,12 +1,12 @@ #!/bin/bash # -# Copyright (C) 2017 The Android Open Source Project +# Copyright 2018 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 +# 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, @@ -14,9 +14,42 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Stop if something fails. +# make us exit on a failure set -e -${DX} --dex --min-sdk-version=28 --output=classes.dex classes +ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-6.0.jar" +INTERMEDIATE_CLASSES=classes-intermediate +TRANSFORMER_CLASSES=classes-transformer +CLASSES=classes -zip $TEST_NAME.jar classes.dex +DEXER="${DX:-dx}" +if [ "${USE_D8=false}" = "true" ]; then + DEXER="${ANDROID_HOST_OUT}/bin/d8-compat-dx" +fi + +# Create directories for classes +for class_dir in "${INTERMEDIATE_CLASSES}" "${TRANSFORMER_CLASSES}" "${CLASSES}"; do + rm -rf "${class_dir}" + mkdir "${class_dir}" +done + +# Build transformer +${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d ${TRANSFORMER_CLASSES} $(find util-src -name '*.java') + +# Generate intermediate classes that will allow transform to be applied to test classes +JAVAC_ARGS="${JAVAC_ARGS} -source 1.8 -target 1.8" +${JAVAC:-javac} ${JAVAC_ARGS} -cp ${TRANSFORMER_CLASSES} -d ${INTERMEDIATE_CLASSES} $(find src -name '*.java') + +# Run transform +for class in ${INTERMEDIATE_CLASSES}/*.class ; do + transformed_class=${CLASSES}/$(basename ${class}) + ${JAVA:-java} -cp "${ASM_JAR}:${TRANSFORMER_CLASSES}" \ + transformer.ConstantTransformer ${class} ${transformed_class} +done + +# Create DEX +DX_FLAGS="${DX_FLAGS} --min-sdk-version=28 --debug --dump-width=1000" +${DEXER} -JXmx256m --dex ${DX_FLAGS} --dump-to=${CLASSES}.lst --output=classes.dex ${CLASSES} ${TRANSFORMER_CLASSES} + +# Zip DEX to file name expected by test runner +zip ${TEST_NAME:-classes-dex}.jar classes.dex diff --git a/test/979-const-method-handle/classes/Main.class b/test/979-const-method-handle/classes/Main.class Binary files differdeleted file mode 100644 index 8d6b7d88bb..0000000000 --- a/test/979-const-method-handle/classes/Main.class +++ /dev/null diff --git a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class b/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class Binary files differdeleted file mode 100644 index a21b0a336c..0000000000 --- a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class +++ /dev/null diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index 573b80da99..bc943e368e 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -1,2 +1,6 @@ -MethodHandle MethodHandle(Object)Class => class java.lang.Float -MethodType (char,short,int,long,float,double,Object)boolean +(int,Integer,System)String +Hello World! And Hello Zog +Hello World! And Hello Zorba +name is HoverFly +2.718281828459045 +Attempting to set Math.E raised IAE diff --git a/test/979-const-method-handle/info.txt b/test/979-const-method-handle/info.txt index e8514ce91f..fc909db299 100644 --- a/test/979-const-method-handle/info.txt +++ b/test/979-const-method-handle/info.txt @@ -1,7 +1 @@ This test checks const-method-handle and const-method-type bytecodes. - -The class files in this test come from: - - dalvik/dx/tests/142-const-method-handle - -and are built using ASM bytecode manipulation library. diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java new file mode 100644 index 0000000000..663814f232 --- /dev/null +++ b/test/979-const-method-handle/src/Main.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 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 annotations.ConstantMethodHandle; +import annotations.ConstantMethodType; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; + +class Main { + private static String name = "default"; + + private static void unreachable() { + throw new Error("Unreachable"); + } + + @ConstantMethodType( + returnType = String.class, + parameterTypes = {int.class, Integer.class, System.class} + ) + private static MethodType methodType0() { + unreachable(); + return null; + } + + static void helloWorld(String who) { + System.out.print("Hello World! And Hello "); + System.out.println(who); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_STATIC, + owner = "Main", + fieldOrMethodName = "helloWorld", + descriptor = "(Ljava/lang/String;)V" + ) + private static MethodHandle printHelloHandle() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_PUT, + owner = "Main", + fieldOrMethodName = "name", + descriptor = "Ljava/lang/String;" + ) + private static MethodHandle setNameHandle() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_GET, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D" + ) + private static MethodHandle getMathE() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_PUT, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D" + ) + private static MethodHandle putMathE() { + unreachable(); + return null; + } + + public static void main(String[] args) throws Throwable { + System.out.println(methodType0()); + printHelloHandle().invokeExact("Zog"); + printHelloHandle().invokeExact("Zorba"); + setNameHandle().invokeExact("HoverFly"); + System.out.print("name is "); + System.out.println(name); + System.out.println(getMathE().invoke()); + try { + putMathE().invokeExact(Math.PI); + unreachable(); + } catch (IllegalAccessError expected) { + System.out.println("Attempting to set Math.E raised IAE"); + } + } +} diff --git a/test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java b/test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java new file mode 100644 index 0000000000..40785ebc6e --- /dev/null +++ b/test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 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. + */ + +package annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be set on method to specify that if this method + * is statically invoked then the invocation is replaced by a + * load-constant bytecode with the MethodHandle constant described by + * the annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ConstantMethodHandle { + /* Method handle kinds */ + public static final int STATIC_PUT = 0; + public static final int STATIC_GET = 1; + public static final int INSTANCE_PUT = 2; + public static final int INSTANCE_GET = 3; + public static final int INVOKE_STATIC = 4; + public static final int INVOKE_VIRTUAL = 5; + public static final int INVOKE_SPECIAL = 6; + public static final int NEW_INVOKE_SPECIAL = 7; + public static final int INVOKE_INTERFACE = 8; + + /** Kind of method handle. */ + int kind(); + + /** Class name owning the field or method. */ + String owner(); + + /** The field or method name addressed by the MethodHandle. */ + String fieldOrMethodName(); + + /** Descriptor for the field (type) or method (method-type) */ + String descriptor(); + + /** Whether the owner is an interface. */ + boolean ownerIsInterface() default false; +} diff --git a/test/979-const-method-handle/util-src/annotations/ConstantMethodType.java b/test/979-const-method-handle/util-src/annotations/ConstantMethodType.java new file mode 100644 index 0000000000..c89fa013fe --- /dev/null +++ b/test/979-const-method-handle/util-src/annotations/ConstantMethodType.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 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. + */ + +package annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be set on method to specify that if this method + * is statically invoked then the invocation is replaced by a + * load-constant bytecode with the MethodType constant described by + * the annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ConstantMethodType { + /** Return type of method() or field getter() */ + Class<?> returnType() default void.class; + + /** Types of parameters for method or field setter() */ + Class<?>[] parameterTypes() default {}; +} diff --git a/test/979-const-method-handle/util-src/transformer/ConstantTransformer.java b/test/979-const-method-handle/util-src/transformer/ConstantTransformer.java new file mode 100644 index 0000000000..9356426492 --- /dev/null +++ b/test/979-const-method-handle/util-src/transformer/ConstantTransformer.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2018 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. + */ + +package transformer; + +import annotations.ConstantMethodHandle; +import annotations.ConstantMethodType; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Class for transforming invoke static bytecodes into constant method handle loads and and constant + * method type loads. + * + * <p>When a parameterless private static method returning a MethodHandle is defined and annotated + * with {@code ConstantMethodHandle}, this transformer will replace static invocations of the method + * with a load constant bytecode with a method handle in the constant pool. + * + * <p>Suppose a method is annotated as: <code> + * @ConstantMethodHandle( + * kind = ConstantMethodHandle.STATIC_GET, + * owner = "java/lang/Math", + * fieldOrMethodName = "E", + * descriptor = "D" + * ) + * private static MethodHandle getMathE() { + * unreachable(); + * return null; + * } + * </code> Then invocations of {@code getMathE} will be replaced by a load from the constant pool + * with the constant method handle described in the {@code ConstantMethodHandle} annotation. + * + * <p>Similarly, a parameterless private static method returning a {@code MethodType} and annotated + * with {@code ConstantMethodType}, will have invocations replaced by a load constant bytecode with + * a method type in the constant pool. + */ +class ConstantTransformer { + static class ConstantBuilder extends ClassVisitor { + private final Map<String, ConstantMethodHandle> constantMethodHandles; + private final Map<String, ConstantMethodType> constantMethodTypes; + + ConstantBuilder( + int api, + ClassVisitor cv, + Map<String, ConstantMethodHandle> constantMethodHandles, + Map<String, ConstantMethodType> constantMethodTypes) { + super(api, cv); + this.constantMethodHandles = constantMethodHandles; + this.constantMethodTypes = constantMethodTypes; + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); + return new MethodVisitor(this.api, mv) { + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == org.objectweb.asm.Opcodes.INVOKESTATIC) { + ConstantMethodHandle constantMethodHandle = constantMethodHandles.get(name); + if (constantMethodHandle != null) { + insertConstantMethodHandle(constantMethodHandle); + return; + } + ConstantMethodType constantMethodType = constantMethodTypes.get(name); + if (constantMethodType != null) { + insertConstantMethodType(constantMethodType); + return; + } + } + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + + private Type buildMethodType(Class<?> returnType, Class<?>[] parameterTypes) { + Type rType = Type.getType(returnType); + Type[] pTypes = new Type[parameterTypes.length]; + for (int i = 0; i < pTypes.length; ++i) { + pTypes[i] = Type.getType(parameterTypes[i]); + } + return Type.getMethodType(rType, pTypes); + } + + private int getHandleTag(int kind) { + switch (kind) { + case ConstantMethodHandle.STATIC_PUT: + return Opcodes.H_PUTSTATIC; + case ConstantMethodHandle.STATIC_GET: + return Opcodes.H_GETSTATIC; + case ConstantMethodHandle.INSTANCE_PUT: + return Opcodes.H_PUTFIELD; + case ConstantMethodHandle.INSTANCE_GET: + return Opcodes.H_GETFIELD; + case ConstantMethodHandle.INVOKE_STATIC: + return Opcodes.H_INVOKESTATIC; + case ConstantMethodHandle.INVOKE_VIRTUAL: + return Opcodes.H_INVOKEVIRTUAL; + case ConstantMethodHandle.INVOKE_SPECIAL: + return Opcodes.H_INVOKESPECIAL; + case ConstantMethodHandle.NEW_INVOKE_SPECIAL: + return Opcodes.H_NEWINVOKESPECIAL; + case ConstantMethodHandle.INVOKE_INTERFACE: + return Opcodes.H_INVOKEINTERFACE; + } + throw new Error("Unhandled kind " + kind); + } + + private void insertConstantMethodHandle(ConstantMethodHandle constantMethodHandle) { + Handle handle = + new Handle( + getHandleTag(constantMethodHandle.kind()), + constantMethodHandle.owner(), + constantMethodHandle.fieldOrMethodName(), + constantMethodHandle.descriptor(), + constantMethodHandle.ownerIsInterface()); + mv.visitLdcInsn(handle); + } + + private void insertConstantMethodType(ConstantMethodType constantMethodType) { + Type methodType = + buildMethodType( + constantMethodType.returnType(), + constantMethodType.parameterTypes()); + mv.visitLdcInsn(methodType); + } + }; + } + } + + private static void throwAnnotationError( + Method method, Class<?> annotationClass, String reason) { + StringBuilder sb = new StringBuilder(); + sb.append("Error in annotation ") + .append(annotationClass) + .append(" on method ") + .append(method) + .append(": ") + .append(reason); + throw new Error(sb.toString()); + } + + private static void checkMethodToBeReplaced( + Method method, Class<?> annotationClass, Class<?> returnType) { + final int PRIVATE_STATIC = Modifier.STATIC | Modifier.PRIVATE; + if ((method.getModifiers() & PRIVATE_STATIC) != PRIVATE_STATIC) { + throwAnnotationError(method, annotationClass, " method is not private and static"); + } + if (method.getTypeParameters().length != 0) { + throwAnnotationError(method, annotationClass, " method expects parameters"); + } + if (!method.getReturnType().equals(returnType)) { + throwAnnotationError(method, annotationClass, " wrong return type"); + } + } + + private static void transform(Path inputClassPath, Path outputClassPath) throws Throwable { + Path classLoadPath = inputClassPath.toAbsolutePath().getParent(); + URLClassLoader classLoader = + new URLClassLoader(new URL[] {classLoadPath.toUri().toURL()}, + ClassLoader.getSystemClassLoader()); + String inputClassName = inputClassPath.getFileName().toString().replace(".class", ""); + Class<?> inputClass = classLoader.loadClass(inputClassName); + + final Map<String, ConstantMethodHandle> constantMethodHandles = new HashMap<>(); + final Map<String, ConstantMethodType> constantMethodTypes = new HashMap<>(); + + for (Method m : inputClass.getDeclaredMethods()) { + ConstantMethodHandle constantMethodHandle = m.getAnnotation(ConstantMethodHandle.class); + if (constantMethodHandle != null) { + checkMethodToBeReplaced(m, ConstantMethodHandle.class, MethodHandle.class); + constantMethodHandles.put(m.getName(), constantMethodHandle); + continue; + } + + ConstantMethodType constantMethodType = m.getAnnotation(ConstantMethodType.class); + if (constantMethodType != null) { + checkMethodToBeReplaced(m, ConstantMethodType.class, MethodType.class); + constantMethodTypes.put(m.getName(), constantMethodType); + continue; + } + } + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + try (InputStream is = Files.newInputStream(inputClassPath)) { + ClassReader cr = new ClassReader(is); + ConstantBuilder cb = + new ConstantBuilder( + Opcodes.ASM6, cw, constantMethodHandles, constantMethodTypes); + cr.accept(cb, 0); + } + try (OutputStream os = Files.newOutputStream(outputClassPath)) { + os.write(cw.toByteArray()); + } + } + + public static void main(String[] args) throws Throwable { + transform(Paths.get(args[0]), Paths.get(args[1])); + } +} diff --git a/test/Android.bp b/test/Android.bp index 17a50421a8..0c1edcaab8 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -429,6 +429,7 @@ cc_defaults { "596-app-images/app_images.cc", "596-monitor-inflation/monitor_inflation.cc", "597-deopt-new-string/deopt.cc", + "616-cha-unloading/cha_unload.cc", "626-const-class-linking/clear_dex_cache_types.cc", "642-fp-callees/fp_callees.cc", "647-jni-get-field-id/get_field_id.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index d1855dcbc7..a7e76d131e 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -75,13 +75,6 @@ "--relocate"] }, { - "tests": "137-cfi", - "variant": "interp-ac", - "description": ["Temporarily disable some broken tests when forcing", - "access checks in interpreter"], - "bug": "http://b/22414682" - }, - { "tests" : "629-vdex-speed", "variant": "interp-ac | no-dex2oat | interpreter | jit | relocate-npatchoat", "description": "629 requires compilation." @@ -346,8 +339,7 @@ { "tests": ["537-checker-arraycopy", "641-checker-arraycopy"], - "env_vars": {"ART_USE_READ_BARRIER": "true"}, - "variant": "interpreter | optimizing | regalloc_gc | jit" + "env_vars": {"ART_READ_BARRIER_TYPE": "TABLELOOKUP"} }, { "tests": ["476-clinit-inline-static-invoke", @@ -958,7 +950,18 @@ ], "variant": "jvm", "bug": "b/73888836", - "description": ["Failing on JVM. Needs further investigating."] + "description": ["Failing on RI. Needs further investigating."] + }, + { + "tests": ["616-cha-unloading", + "678-quickening"], + "variant": "jvm", + "description": ["Doesn't run on RI."] + }, + { + "tests": ["616-cha-unloading"], + "variant": "trace", + "description": ["Trace prevents class unloading."] }, { "tests": "677-fsi", diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 53c6fb63f9..de07a47df7 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -206,10 +206,7 @@ else if [[ "$mode" == "host" ]]; then dump_command="/bin/kill -3" else - # TODO It would be great to be able to use this on target too but we need to - # be able to walk /proc to figure out what the actual child pid is. - dump_command="/system/bin/true" - # dump_command="/system/xbin/su root /data/local/tmp/system/bin/debuggerd -b" + dump_command="/system/xbin/su root /data/local/tmp/system/bin/debuggerd" fi if [[ $has_gdb = "yes" ]]; then if [[ $mode == "target" ]]; then diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 546a6bf55b..9373c69bf8 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -57,6 +57,16 @@ echo -e "${green}Setting local loopback${nc}" adb shell ifconfig lo up adb shell ifconfig +# When netd is running, some libcore and JDWP tests fail with this +# exception (b/74725685): +# +# android.system.ErrnoException: connect failed: EBADMSG (Not a data message) +# +# Turn it off to make these tests pass. +echo -e "${green}Turning off netd${nc}" +adb shell stop netd +adb shell getprop init.svc.netd + echo -e "${green}List properties${nc}" adb shell getprop |