summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_generator_vector_arm64.cc138
-rw-r--r--compiler/optimizing/code_generator_vector_arm_vixl.cc126
-rw-r--r--compiler/optimizing/code_generator_vector_mips.cc90
-rw-r--r--compiler/optimizing/code_generator_vector_mips64.cc90
-rw-r--r--compiler/optimizing/code_generator_vector_x86.cc140
-rw-r--r--compiler/optimizing/code_generator_vector_x86_64.cc140
-rw-r--r--compiler/optimizing/instruction_simplifier.cc31
-rw-r--r--compiler/optimizing/loop_optimization.cc143
-rw-r--r--compiler/optimizing/loop_optimization.h6
-rw-r--r--compiler/optimizing/nodes.h2
-rw-r--r--compiler/optimizing/nodes_vector.h52
-rw-r--r--dex2oat/dex2oat_test.cc37
-rw-r--r--dexlayout/compact_dex_writer.cc5
-rw-r--r--dexlayout/compact_dex_writer.h4
-rw-r--r--libdexfile/dex/compact_dex_file.h14
-rw-r--r--libdexfile/dex/dex_file-inl.h12
-rw-r--r--libdexfile/dex/dex_file.h21
-rw-r--r--oatdump/oatdump.cc5
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S16
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S14
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S24
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S16
-rw-r--r--runtime/art_method-inl.h5
-rw-r--r--runtime/art_method.cc26
-rw-r--r--runtime/art_method.h18
-rw-r--r--runtime/cha.cc101
-rw-r--r--runtime/cha.h11
-rw-r--r--runtime/class_linker.cc77
-rw-r--r--runtime/class_linker.h2
-rw-r--r--runtime/class_table-inl.h8
-rw-r--r--runtime/class_table.h4
-rw-r--r--runtime/common_throws.cc50
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h5
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc92
-rw-r--r--runtime/hidden_api.h47
-rw-r--r--runtime/interpreter/cfi_asm_support.h52
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc54
-rw-r--r--runtime/interpreter/interpreter_switch_impl.h48
-rw-r--r--runtime/interpreter/mterp/arm/header.S2
-rw-r--r--runtime/interpreter/mterp/arm64/header.S2
-rw-r--r--runtime/interpreter/mterp/cfi_asm_support.h47
-rwxr-xr-xruntime/interpreter/mterp/gen_mterp.py2
-rw-r--r--runtime/interpreter/mterp/mips/header.S2
-rw-r--r--runtime/interpreter/mterp/mips64/header.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm64.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_mips.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_mips64.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86_64.S2
-rw-r--r--runtime/interpreter/mterp/x86/header.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/header.S2
-rw-r--r--runtime/interpreter/unstarted_runtime.cc4
-rw-r--r--runtime/jit/debugger_interface.cc140
-rw-r--r--runtime/mirror/class-inl.h23
-rw-r--r--runtime/mirror/class.h6
-rw-r--r--runtime/mirror/iftable.h5
-rw-r--r--runtime/oat_file_assistant.cc44
-rw-r--r--runtime/vdex_file.h3
-rw-r--r--runtime/verifier/method_verifier-inl.h2
-rw-r--r--runtime/verifier/method_verifier.cc175
-rw-r--r--runtime/verifier/method_verifier.h31
-rw-r--r--test/137-cfi/cfi.cc7
-rw-r--r--test/616-cha-unloading/cha_unload.cc53
-rw-r--r--test/616-cha-unloading/expected.txt2
-rw-r--r--test/616-cha-unloading/info.txt1
-rw-r--r--test/616-cha-unloading/run18
-rw-r--r--test/616-cha-unloading/src-art/AbstractCHATester.java19
-rw-r--r--test/616-cha-unloading/src-art/Main.java121
-rw-r--r--test/616-cha-unloading/src-ex/AbstractCHATester.java19
-rw-r--r--test/616-cha-unloading/src-ex/ConcreteCHATester.java19
-rw-r--r--test/623-checker-loop-regressions/src/Main.java14
-rw-r--r--test/674-hiddenapi/api-blacklist.txt2
-rw-r--r--test/674-hiddenapi/api-dark-greylist.txt2
-rw-r--r--test/674-hiddenapi/api-light-greylist.txt2
-rw-r--r--test/674-hiddenapi/src-ex/ChildClass.java38
-rw-r--r--test/674-hiddenapi/src-ex/Linking.java37
-rw-r--r--test/674-hiddenapi/src-ex/OverrideClass.java35
-rw-r--r--test/674-hiddenapi/src/ParentClass.java27
-rw-r--r--test/678-checker-simd-saturation/expected.txt1
-rw-r--r--test/678-checker-simd-saturation/info.txt1
-rw-r--r--test/678-checker-simd-saturation/src/Main.java389
-rw-r--r--test/678-quickening/expected.txt1
-rw-r--r--test/678-quickening/info.txt1
-rw-r--r--test/678-quickening/run18
-rw-r--r--test/678-quickening/src-art/Main.java79
-rw-r--r--test/679-checker-minmax/expected.txt1
-rw-r--r--test/679-checker-minmax/info.txt1
-rw-r--r--test/679-checker-minmax/src/Main.java159
-rwxr-xr-x[-rw-r--r--]test/979-const-method-handle/build43
-rw-r--r--test/979-const-method-handle/classes/Main.classbin943 -> 0 bytes
-rw-r--r--test/979-const-method-handle/classes/constmethodhandle/ConstTest.classbin1411 -> 0 bytes
-rw-r--r--test/979-const-method-handle/expected.txt8
-rw-r--r--test/979-const-method-handle/info.txt6
-rw-r--r--test/979-const-method-handle/src/Main.java102
-rw-r--r--test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java58
-rw-r--r--test/979-const-method-handle/util-src/annotations/ConstantMethodType.java38
-rw-r--r--test/979-const-method-handle/util-src/transformer/ConstantTransformer.java229
-rw-r--r--test/Android.bp1
-rw-r--r--test/knownfailures.json23
-rwxr-xr-xtools/run-jdwp-tests.sh5
-rwxr-xr-xtools/setup-buildbot-device.sh10
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_ = &reg_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
deleted file mode 100644
index 8d6b7d88bb..0000000000
--- a/test/979-const-method-handle/classes/Main.class
+++ /dev/null
Binary files differ
diff --git a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class b/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class
deleted file mode 100644
index a21b0a336c..0000000000
--- a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class
+++ /dev/null
Binary files differ
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